Ejemplo n.º 1
0
Archivo: _vc.py Proyecto: yuecong/meld
class Entry(object):
    # These are labels for possible states of version controlled files;
    # not all states have a label to avoid visual clutter.
    state_names = {
        STATE_IGNORED: _("Ignored"),
        STATE_NONE: _("Unversioned"),
        STATE_NORMAL: "",
        STATE_NOCHANGE: "",
        STATE_ERROR: _("Error"),
        STATE_EMPTY: "",
        STATE_NEW: _("Newly added"),
        STATE_MODIFIED: _("Modified"),
        STATE_RENAMED: _("Renamed"),
        STATE_CONFLICT: "<b>%s</b>" % _("Conflict"),
        STATE_REMOVED: _("Removed"),
        STATE_MISSING: _("Missing"),
        STATE_NONEXIST: _("Not present"),
    }

    def __init__(self, path, name, state):
        self.path = path
        self.state = state
        self.parent, self.name = os.path.split(path.rstrip("/"))

    def __str__(self):
        return "<%s:%s %s>" % (self.__class__.__name__, self.path,
                               self.get_status() or "Normal")

    def __repr__(self):
        return "%s(%r, %r, %r)" % (self.__class__.__name__, self.name,
                                   self.path, self.state)

    def get_status(self):
        return self.state_names[self.state]
Ejemplo n.º 2
0
class Entry(object):
    # These are labels for possible states of version controlled files;
    # not all states have a label to avoid visual clutter.
    state_names = {
        STATE_IGNORED: _("Ignored"),
        STATE_NONE: _("Unversioned"),
        STATE_NORMAL: "",
        STATE_NOCHANGE: "",
        STATE_ERROR: _("Error"),
        STATE_EMPTY: "",
        STATE_NEW: _("Newly added"),
        STATE_MODIFIED: _("Modified"),
        STATE_RENAMED: _("Renamed"),
        STATE_CONFLICT: "<b>%s</b>" % _("Conflict"),
        STATE_REMOVED: _("Removed"),
        STATE_MISSING: _("Missing"),
        STATE_NONEXIST: _("Not present"),
    }

    def __init__(self, path, name, state, isdir, options=None):
        self.path = path
        self.name = name
        self.state = state
        self.isdir = isdir
        if isinstance(options, list):
            options = ','.join(options)
        self.options = options

    def __str__(self):
        return "<%s:%s %s>" % (self.__class__.__name__, self.path,
                               self.get_status() or "Normal")

    def __repr__(self):
        return "%s(%r, %r, %r)" % (self.__class__.__name__, self.name,
                                   self.path, self.state)

    def get_status(self):
        return self.state_names[self.state]

    def is_present(self):
        """Should this Entry actually be present on the file system"""
        return self.state not in (STATE_REMOVED, STATE_MISSING)

    @staticmethod
    def is_modified(entry):
        return entry.state >= STATE_NEW or (entry.isdir and
                                            (entry.state > STATE_NONE))

    @staticmethod
    def is_normal(entry):
        return entry.state == STATE_NORMAL

    @staticmethod
    def is_nonvc(entry):
        return entry.state == STATE_NONE or (entry.isdir and
                                             (entry.state > STATE_IGNORED))

    @staticmethod
    def is_ignored(entry):
        return entry.state == STATE_IGNORED or entry.isdir
Ejemplo n.º 3
0
    def setup_mac_integration(self, menubar):
        self.set_use_quartz_accelerators(True)
        self.set_menu_bar(menubar)

        item = Gtk.MenuItem.new_with_label(_("About"))
        item.connect("activate", self.about_callback, None)
        menubar.add(item)
        self.insert_app_menu_item(item, 0)
        self.set_about_item(item)

        separator = Gtk.SeparatorMenuItem()
        menubar.add(separator)
        self.insert_app_menu_item(separator, 1)

        item = Gtk.MenuItem.new_with_label(_("Preferences"))
        item.connect("activate", self.preferences_callback, None)
        menubar.add(item)
        self.insert_app_menu_item(item, 2)

        item = Gtk.MenuItem.new_with_label(_("Shell Integration"))
        item.connect("activate", self.mac_shell_integration_callback, None)
        menubar.add(item)
        self.insert_app_menu_item(item, 3)

        separator = Gtk.SeparatorMenuItem()
        menubar.add(separator)
        self.insert_app_menu_item(separator, 4)

        self.sync_menubar()

        self.ready()
Ejemplo n.º 4
0
 def __init__(self):
     GObject.GObject.__init__(self)
     self.scheduler = task.FifoScheduler()
     self.num_panes = 0
     self.label_text = _("untitled")
     self.tooltip_text = _("untitled")
     self.main_actiongroup = None
Ejemplo n.º 5
0
    def on_button_remove_clicked(self, obj):
        selected = self._get_selected_files()
        if any(os.path.isdir(p) for p in selected):
            # TODO: Improve and reuse this dialog for the non-VC delete action
            dialog = Gtk.MessageDialog(
                parent=self.widget.get_toplevel(),
                flags=Gtk.DialogFlags.MODAL
                | Gtk.DialogFlags.DESTROY_WITH_PARENT,
                type=Gtk.MessageType.WARNING,
                message_format=_("Remove folder and all its files?"))
            dialog.format_secondary_text(
                _("This will remove all selected files and folders, and all "
                  "files within any selected folders, from version control."))

            dialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
            dialog.add_button(_("_Remove"), Gtk.ResponseType.OK)
            response = dialog.run()
            dialog.destroy()
            if response != Gtk.ResponseType.OK:
                return

        try:
            self.vc.remove(self._command, selected)
        except NotImplementedError:
            self._command_on_selected(self.vc.remove_command())
Ejemplo n.º 6
0
 def __init__(self):
     GObject.GObject.__init__(self)
     self.scheduler = task.FifoScheduler()
     self.num_panes = 0
     self.label_text = _("untitled")
     self.tooltip_text = _("untitled")
     self.main_actiongroup = None
Ejemplo n.º 7
0
    def setup_mac_integration(self, menubar):
            from Cocoa import NSApp
            self.set_use_quartz_accelerators(True)
            self.set_menu_bar(menubar)

            item = Gtk.MenuItem.new_with_label(_("About"))
            item.connect("activate", self.about_callback, None)
            menubar.add(item)
            self.insert_app_menu_item(item, 0)
            self.set_about_item(item)
            
            separator = Gtk.SeparatorMenuItem()
            menubar.add(separator)
            self.insert_app_menu_item(separator, 1)

            item = Gtk.MenuItem.new_with_label(_("Preferences"))
            item.connect("activate", self.preferences_callback, None)
            menubar.add(item)
            self.insert_app_menu_item(item, 2)

            item = Gtk.MenuItem.new_with_label(_("Shell Integration"))
            item.connect("activate", self.mac_shell_integration_callback, None)
            menubar.add(item)
            self.insert_app_menu_item(item, 3)

            separator = Gtk.SeparatorMenuItem()
            menubar.add(separator)
            self.insert_app_menu_item(separator, 4)

            self.sync_menubar()

            self.ready()
            NSApp.activateIgnoringOtherApps_(True)
            self.attention_request(GtkosxApplication.ApplicationAttentionType.NFO_REQUEST)
Ejemplo n.º 8
0
 def recompute_label(self):
     location = self.location
     if isinstance(location, str):
         location = location.decode(sys.getfilesystemencoding(), 'replace')
     self.label_text = os.path.basename(location)
     # TRANSLATORS: This is the location of the directory the user is diffing
     self.tooltip_text = _("%s: %s") % (_("Location"), location)
     self.label_changed()
Ejemplo n.º 9
0
    def populate_vcs_for_location(self, location):
        """Display VC plugin(s) that can handle the location"""
        vcs_model = self.combobox_vcs.get_model()
        vcs_model.clear()

        # VC systems can be executed at the directory level, so make sure
        # we're checking for VC support there instead of
        # on a specific file or on deleted/unexisting path inside vc
        location = os.path.abspath(location or ".")
        while not os.path.isdir(location):
            parent_location = os.path.dirname(location)
            if len(parent_location) >= len(location):
                # no existing parent: for example unexisting drive on Windows
                break
            location = parent_location
        else:
            # existing parent directory was found
            for avc in get_vcs(location):
                err_str = ''
                vc_details = {'name': avc.NAME, 'cmd': avc.CMD}

                if not avc.is_installed():
                    # Translators: This error message is shown when a version
                    # control binary isn't installed.
                    err_str = _("%(name)s (%(cmd)s not installed)")
                elif not avc.valid_repo(location):
                    # Translators: This error message is shown when a version
                    # controlled repository is invalid.
                    err_str = _("%(name)s (Invalid repository)")

                if err_str:
                    vcs_model.append([err_str % vc_details, avc, False])
                    continue

                vcs_model.append([avc.NAME, avc(location), True])

        valid_vcs = [(i, r[1].NAME) for i, r in enumerate(vcs_model) if r[2]]
        default_active = min(valid_vcs)[0] if valid_vcs else 0

        # Keep the same VC plugin active on refresh, otherwise use the first
        current_vc_name = self.vc.NAME if self.vc else None
        same_vc = [i for i, name in valid_vcs if name == current_vc_name]
        if same_vc:
            default_active = same_vc[0]

        if not valid_vcs:
            # If we didn't get any valid vcs then fallback to null
            null_vcs = _null.Vc(location)
            vcs_model.insert(0, [null_vcs.NAME, null_vcs, True])
            tooltip = _("No valid version control system found in this folder")
        elif len(vcs_model) == 1:
            tooltip = _("Only one version control system found in this folder")
        else:
            tooltip = _("Choose which version control system to use")

        self.combobox_vcs.set_tooltip_text(tooltip)
        self.combobox_vcs.set_sensitive(len(vcs_model) > 1)
        self.combobox_vcs.set_active(default_active)
Ejemplo n.º 10
0
Archivo: vcview.py Proyecto: zbyna/meld
    def populate_vcs_for_location(self, location):
        """Display VC plugin(s) that can handle the location"""
        vcs_model = self.combobox_vcs.get_model()
        vcs_model.clear()

        # VC systems can be executed at the directory level, so make sure
        # we're checking for VC support there instead of
        # on a specific file or on deleted/unexisting path inside vc
        location = os.path.abspath(location or ".")
        while not os.path.isdir(location):
            parent_location = os.path.dirname(location)
            if len(parent_location) >= len(location):
                # no existing parent: for example unexisting drive on Windows
                break
            location = parent_location
        else:
            # existing parent directory was found
            for avc in vc.get_vcs(location):
                err_str = ''
                vc_details = {'name': avc.NAME, 'cmd': avc.CMD}

                if not avc.is_installed():
                    # Translators: This error message is shown when a version
                    # control binary isn't installed.
                    err_str = _("%(name)s (%(cmd)s not installed)")
                elif not avc.valid_repo(location):
                    # Translators: This error message is shown when a version
                    # controlled repository is invalid.
                    err_str = _("%(name)s (Invalid repository)")

                if err_str:
                    vcs_model.append([err_str % vc_details, avc, False])
                    continue

                vcs_model.append([avc.NAME, avc(location), True])

        valid_vcs = [(i, r[1].NAME) for i, r in enumerate(vcs_model) if r[2]]
        default_active = min(valid_vcs)[0] if valid_vcs else 0

        # Keep the same VC plugin active on refresh, otherwise use the first
        current_vc_name = self.vc.NAME if self.vc else None
        same_vc = [i for i, name in valid_vcs if name == current_vc_name]
        if same_vc:
            default_active = same_vc[0]

        if not valid_vcs:
            # If we didn't get any valid vcs then fallback to null
            null_vcs = _null.Vc(location)
            vcs_model.insert(0, [null_vcs.NAME, null_vcs, True])
            tooltip = _("No valid version control system found in this folder")
        elif len(vcs_model) == 1:
            tooltip = _("Only one version control system found in this folder")
        else:
            tooltip = _("Choose which version control system to use")

        self.combobox_vcs.set_tooltip_text(tooltip)
        self.combobox_vcs.set_sensitive(len(vcs_model) > 1)
        self.combobox_vcs.set_active(default_active)
Ejemplo n.º 11
0
Archivo: vcview.py Proyecto: fy2/meld
 def recompute_label(self):
     location = self.location
     if isinstance(location, str):
         location = location.decode(
             sys.getfilesystemencoding(), 'replace')
     self.label_text = os.path.basename(location)
     # TRANSLATORS: This is the location of the directory the user is diffing
     self.tooltip_text = _("%s: %s") % (_("Location"), location)
     self.label_changed()
Ejemplo n.º 12
0
    def _search_recursively_iter(self, iterstart):
        rootname = self.model.value_path(iterstart, 0)
        prefixlen = len(self.location) + 1
        symlinks_followed = set()
        todo = [(self.model.get_path(iterstart), rootname)]

        flattened = self.actiongroup.get_action("VcFlatten").get_active()
        active_action = lambda a: self.actiongroup.get_action(a).get_active()
        filters = [a[1] for a in self.state_actions.values() if
                   active_action(a[0]) and a[1]]

        yield _("Scanning %s") % rootname
        self.vc.cache_inventory(rootname)
        while todo:
            # This needs to happen sorted and depth-first in order for our row
            # references to remain valid while we traverse.
            todo.sort()
            treepath, path = todo.pop(0)
            it = self.model.get_iter(treepath)
            yield _("Scanning %s") % path[prefixlen:]

            entries = self.vc.listdir(path)
            entries = [e for e in entries if any(f(e) for f in filters)]
            for e in entries:
                if e.isdir:
                    try:
                        st = os.lstat(e.path)
                    # Covers certain unreadable symlink cases; see bgo#585895
                    except OSError as err:
                        error_string = "%s: %s" % (e.path, err.strerror)
                        self.model.add_error(it, error_string, 0)
                        continue

                    if stat.S_ISLNK(st.st_mode):
                        key = (st.st_dev, st.st_ino)
                        if key in symlinks_followed:
                            continue
                        symlinks_followed.add(key)

                    if flattened:
                        if e.state != tree.STATE_IGNORED:
                            todo.append((Gtk.TreePath.new_first(), e.path))
                        continue

                child = self.model.add_entries(it, [e.path])
                if e.isdir and e.state != tree.STATE_IGNORED:
                    todo.append((self.model.get_path(child), e.path))
                self._update_item_state(child, e, path[prefixlen:])

            if flattened:
                root = Gtk.TreePath.new_first()
                self.treeview.expand_row(Gtk.TreePath(root), False)
            else:
                if not entries:
                    self.model.add_empty(it, _("(Empty)"))
                if any(e.state != tree.STATE_NORMAL for e in entries):
                    self.treeview.expand_to_path(treepath)
Ejemplo n.º 13
0
 def file_saved_cb(self, saver, result, *args):
     gfile = saver.get_location()
     try:
         saver.save_finish(result)
     except GLib.Error as err:
         filename = GLib.markup_escape_text(gfile.get_parse_name())
         error_dialog(
             primary=_("Could not save file %s.") % filename,
             secondary=_("Couldn’t save file due to:\n%s") %
             (GLib.markup_escape_text(str(err))),
         )
Ejemplo n.º 14
0
 def file_saved_cb(self, saver, result, *args):
     gfile = saver.get_location()
     try:
         saver.save_finish(result)
     except GLib.Error as err:
         filename = GLib.markup_escape_text(gfile.get_parse_name())
         error_dialog(
             primary=_("Could not save file %s.") % filename,
             secondary=_("Couldn’t save file due to:\n%s") % (
                 GLib.markup_escape_text(str(err))),
         )
Ejemplo n.º 15
0
    def populate_vcs_for_location(self, location):
        """Display VC plugin(s) that can handle the location"""
        vcs_model = self.combobox_vcs.get_model()
        vcs_model.clear()

        # VC systems can be executed at the directory level, so make sure
        # we're checking for VC support there instead of
        # on a specific file or on deleted/unexisting path inside vc
        location = os.path.abspath(location or ".")
        while not os.path.isdir(location):
            parent_location = os.path.dirname(location)
            if len(parent_location) >= len(location):
                # no existing parent: for example unexisting drive on Windows
                break
            location = parent_location
        else:
            # existing parent directory was found
            for avc, enabled in get_vcs(location):
                err_str = ''
                vc_details = {'name': avc.NAME, 'cmd': avc.CMD}

                if not enabled:
                    # Translators: This error message is shown when no
                    # repository of this type is found.
                    err_str = _("%(name)s (not found)")
                elif not avc.is_installed():
                    # Translators: This error message is shown when a version
                    # control binary isn't installed.
                    err_str = _("%(name)s (%(cmd)s not installed)")
                elif not avc.valid_repo(location):
                    # Translators: This error message is shown when a version
                    # controlled repository is invalid.
                    err_str = _("%(name)s (invalid repository)")

                if err_str:
                    vcs_model.append([err_str % vc_details, avc, False])
                    continue

                vcs_model.append([avc.NAME, avc(location), True])

        default_active = self.get_default_vc(vcs_model)

        if not any(enabled for _, _, enabled in vcs_model):
            # If we didn't get any valid vcs then fallback to null
            null_vcs = _null.Vc(location)
            vcs_model.insert(0, [null_vcs.NAME, null_vcs, True])
            tooltip = _("No valid version control system found in this folder")
        else:
            tooltip = _("Choose which version control system to use")

        self.combobox_vcs.set_tooltip_text(tooltip)
        self.combobox_vcs.set_active(default_active)
Ejemplo n.º 16
0
Archivo: vcview.py Proyecto: GNOME/meld
    def populate_vcs_for_location(self, location):
        """Display VC plugin(s) that can handle the location"""
        vcs_model = self.combobox_vcs.get_model()
        vcs_model.clear()

        # VC systems can be executed at the directory level, so make sure
        # we're checking for VC support there instead of
        # on a specific file or on deleted/unexisting path inside vc
        location = os.path.abspath(location or ".")
        while not os.path.isdir(location):
            parent_location = os.path.dirname(location)
            if len(parent_location) >= len(location):
                # no existing parent: for example unexisting drive on Windows
                break
            location = parent_location
        else:
            # existing parent directory was found
            for avc, enabled in get_vcs(location):
                err_str = ''
                vc_details = {'name': avc.NAME, 'cmd': avc.CMD}

                if not enabled:
                    # Translators: This error message is shown when no
                    # repository of this type is found.
                    err_str = _("%(name)s (not found)")
                elif not avc.is_installed():
                    # Translators: This error message is shown when a version
                    # control binary isn't installed.
                    err_str = _("%(name)s (%(cmd)s not installed)")
                elif not avc.valid_repo(location):
                    # Translators: This error message is shown when a version
                    # controlled repository is invalid.
                    err_str = _("%(name)s (invalid repository)")

                if err_str:
                    vcs_model.append([err_str % vc_details, avc, False])
                    continue

                vcs_model.append([avc.NAME, avc(location), True])

        default_active = self.get_default_vc(vcs_model)

        if not any(enabled for _, _, enabled in vcs_model):
            # If we didn't get any valid vcs then fallback to null
            null_vcs = _null.Vc(location)
            vcs_model.insert(0, [null_vcs.NAME, null_vcs, True])
            tooltip = _("No valid version control system found in this folder")
        else:
            tooltip = _("Choose which version control system to use")

        self.combobox_vcs.set_tooltip_text(tooltip)
        self.combobox_vcs.set_active(default_active)
Ejemplo n.º 17
0
    def _make_copy_menu(self, chunk):
        copy_menu = Gtk.Menu()
        copy_up = Gtk.MenuItem.new_with_mnemonic(_('Copy _up'))
        copy_down = Gtk.MenuItem.new_with_mnemonic(_('Copy _down'))
        copy_menu.append(copy_up)
        copy_menu.append(copy_down)
        copy_menu.show_all()

        def copy_chunk(widget, action):
            self._action_on_chunk(action, chunk)

        copy_up.connect('activate', copy_chunk, ChunkAction.copy_up)
        copy_down.connect('activate', copy_chunk, ChunkAction.copy_down)
        return copy_menu
Ejemplo n.º 18
0
    def _make_copy_menu(self, chunk):
        copy_menu = Gtk.Menu()
        copy_up = Gtk.MenuItem.new_with_mnemonic(_('Copy _up'))
        copy_down = Gtk.MenuItem.new_with_mnemonic(_('Copy _down'))
        copy_menu.append(copy_up)
        copy_menu.append(copy_down)
        copy_menu.show_all()

        def copy_chunk(widget, action):
            self._action_on_chunk(action, chunk)

        copy_up.connect('activate', copy_chunk, ChunkAction.copy_up)
        copy_down.connect('activate', copy_chunk, ChunkAction.copy_down)
        return copy_menu
Ejemplo n.º 19
0
def prompt_save_filename(title: str,
                         parent: Optional[Gtk.Widget] = None
                         ) -> Optional[Gio.File]:

    dialog = MeldFileChooserDialog(
        title,
        transient_for=get_modal_parent(parent),
        action=Gtk.FileChooserAction.SAVE,
    )
    dialog.set_default_response(Gtk.ResponseType.ACCEPT)
    response = dialog.run()
    gfile = dialog.get_file()
    dialog.destroy()

    if response != Gtk.ResponseType.ACCEPT or not gfile:
        return None

    try:
        file_info = gfile.query_info(
            'standard::name,standard::display-name',
            Gio.FileQueryInfoFlags.NONE,
            None,
        )
    except GLib.Error as err:
        if err.code == Gio.IOErrorEnum.NOT_FOUND:
            return gfile
        raise

    # The selected file exists, so we need to prompt for overwrite.
    parent_folder = gfile.get_parent()
    parent_name = parent_folder.get_parse_name() if parent_folder else ''
    file_name = file_info.get_display_name()

    replace = modal_dialog(
        primary=_("Replace file “%s”?") % file_name,
        secondary=_("A file with this name already exists in “%s”.\n"
                    "If you replace the existing file, its contents "
                    "will be lost.") % parent_name,
        buttons=[
            (_("_Cancel"), Gtk.ResponseType.CANCEL),
            (_("_Replace"), Gtk.ResponseType.OK),
        ],
        messagetype=Gtk.MessageType.WARNING,
    )
    if replace != Gtk.ResponseType.OK:
        return None

    return gfile
Ejemplo n.º 20
0
    def _update_notebook_menu(self, *args):
        if self.tab_switch_merge_id:
            self.ui.remove_ui(self.tab_switch_merge_id)
            self.ui.remove_action_group(self.tab_switch_actiongroup)

        self.tab_switch_merge_id = self.ui.new_merge_id()
        self.tab_switch_actiongroup = Gtk.ActionGroup(name="TabSwitchActions")
        self.ui.insert_action_group(self.tab_switch_actiongroup)
        group = None
        current_page = self.notebook.get_current_page()
        for i in range(self.notebook.get_n_pages()):
            page = self.notebook.get_nth_page(i)
            label = self.notebook.get_menu_label_text(page) or ""
            label = label.replace("_", "__")
            name = "SwitchTab%d" % i
            tooltip = _("Switch to this tab")
            action = Gtk.RadioAction(name=name, label=label, tooltip=tooltip, stock_id=None, value=i)
            action.join_group(group)
            group = action
            action.set_active(current_page == i)

            def current_tab_changed_cb(action, current):
                if action == current:
                    self.notebook.set_current_page(action.get_current_value())
            action.connect("changed", current_tab_changed_cb)
            if i < 10:
                accel = "<Alt>%d" % ((i + 1) % 10)
            else:
                accel = None
            self.tab_switch_actiongroup.add_action_with_accel(action, accel)
            self.ui.add_ui(self.tab_switch_merge_id,
                           "/Menubar/TabMenu/TabPlaceholder",
                           name, name, Gtk.UIManagerItemType.MENUITEM, False)
Ejemplo n.º 21
0
Archivo: misc.py Proyecto: GNOME/meld
def shorten_names(*names: str) -> List[str]:
    """Remove common parts of a list of paths

    For example, `('/tmp/foo1', '/tmp/foo2')` would be summarised as
    `('foo1', 'foo2')`. Paths that share a basename are distinguished
    by prepending an indicator, e.g., `('/a/b/c', '/a/d/c')` would be
    summarised to `['[b] c', '[d] c']`.
    """

    paths = [PurePath(n) for n in names]

    # Identify the longest common path among the list of path
    common = set(paths[0].parents)
    common = common.intersection(*(p.parents for p in paths))
    if not common:
        return list(names)
    common_parent = sorted(common, key=lambda p: -len(p.parts))[0]

    paths = [p.relative_to(common_parent) for p in paths]
    basenames = [p.name for p in paths]

    if all_same(basenames):
        def firstpart(path: PurePath) -> str:
            if len(path.parts) > 1 and path.parts[0]:
                return "[%s] " % path.parts[0]
            else:
                return ""
        return [firstpart(p) + p.name for p in paths]

    return [name or _("[None]") for name in basenames]
Ejemplo n.º 22
0
def colour_lookup_with_fallback(name, attribute):
    from meld.settings import meldsettings
    source_style = meldsettings.style_scheme

    style = source_style.get_style(name)
    style_attr = getattr(style.props, attribute) if style else None
    if not style or not style_attr:
        manager = GtkSource.StyleSchemeManager.get_default()
        source_style = manager.get_scheme(MELD_STYLE_SCHEME)
        try:
            style = source_style.get_style(name)
            style_attr = getattr(style.props, attribute)
        except AttributeError:
            pass

    if not style_attr:
        import sys
        print >> sys.stderr, _(
            "Couldn't find colour scheme details for %s-%s; "
            "this is a bad install") % (name, attribute)
        sys.exit(1)

    colour = Gdk.RGBA()
    colour.parse(style_attr)
    return colour
Ejemplo n.º 23
0
    def run(self):
        self.update_patch()

        while 1:
            result = self.widget.run()
            if result < 0:
                break

            buf = self.textview.get_buffer()
            start, end = buf.get_bounds()
            txt = text_type(buf.get_text(start, end, False), 'utf8')

            # Copy patch to clipboard
            if result == 1:
                clip = Gtk.clipboard_get()
                clip.set_text(txt)
                clip.store()
                break
            # Save patch as a file
            else:
                # FIXME: These filediff methods are actually general utility.
                filename = self.filediff._get_filename_for_saving(
                    _("Save Patch"))
                if filename:
                    txt = txt.encode('utf-8')
                    self.filediff._save_text_to_filename(filename, txt)
                    break

        self.widget.hide()
Ejemplo n.º 24
0
def shorten_names(*names):
    """Remove redunant parts of a list of names (e.g. /tmp/foo{1,2} -> foo{1,2}
    """
    # TODO: Update for different path separators and URIs
    prefix = os.path.commonprefix(names)
    prefixslash = prefix.rfind("/") + 1

    names = [n[prefixslash:] for n in names]
    paths = [n.split("/") for n in names]

    try:
        basenames = [p[-1] for p in paths]
    except IndexError:
        pass
    else:
        if all_same(basenames):

            def firstpart(alist):
                if len(alist) > 1 and alist[0]:
                    return "[%s] " % alist[0]
                else:
                    return ""

            roots = [firstpart(p) for p in paths]
            base = basenames[0].strip()
            return [r + base for r in roots]
    # no common path. empty names get changed to "[None]"
    return [name or _("[None]") for name in basenames]
Ejemplo n.º 25
0
 def append_diff(self,
                 gfiles,
                 auto_compare=False,
                 auto_merge=False,
                 merge_output=None,
                 meta=None):
     have_directories = False
     have_files = False
     for f in gfiles:
         if f.query_file_type(Gio.FileQueryInfoFlags.NONE,
                              None) == Gio.FileType.DIRECTORY:
             have_directories = True
         else:
             have_files = True
     if have_directories and have_files:
         raise ValueError(
             _("Cannot compare a mixture of files and directories"))
     elif have_directories:
         return self.append_dirdiff(gfiles, auto_compare)
     elif auto_merge:
         return self.append_filemerge(gfiles, merge_output=merge_output)
     else:
         return self.append_filediff(gfiles,
                                     merge_output=merge_output,
                                     meta=meta)
Ejemplo n.º 26
0
Archivo: misc.py Proyecto: ssalexa/meld
def shorten_names(*names: str) -> List[str]:
    """Remove common parts of a list of paths

    For example, `('/tmp/foo1', '/tmp/foo2')` would be summarised as
    `('foo1', 'foo2')`. Paths that share a basename are distinguished
    by prepending an indicator, e.g., `('/a/b/c', '/a/d/c')` would be
    summarised to `['[b] c', '[d] c']`.
    """

    paths = [PurePath(n) for n in names]

    # Identify the longest common path among the list of path
    common = set(paths[0].parents)
    common = common.intersection(*(p.parents for p in paths))
    if not common:
        return list(names)
    common_parent = sorted(common, key=lambda p: -len(p.parts))[0]

    paths = [p.relative_to(common_parent) for p in paths]
    basenames = [p.name for p in paths]

    if all_same(basenames):

        def firstpart(path: PurePath) -> str:
            if len(path.parts) > 1 and path.parts[0]:
                return "[%s] " % path.parts[0]
            else:
                return ""

        return [firstpart(p) + p.name for p in paths]

    return [name or _("[None]") for name in basenames]
Ejemplo n.º 27
0
    def _update_notebook_menu(self, *args):
        if self.tab_switch_merge_id:
            self.ui.remove_ui(self.tab_switch_merge_id)
            self.ui.remove_action_group(self.tab_switch_actiongroup)

        self.tab_switch_merge_id = self.ui.new_merge_id()
        self.tab_switch_actiongroup = Gtk.ActionGroup(name="TabSwitchActions")
        self.ui.insert_action_group(self.tab_switch_actiongroup)
        group = None
        current_page = self.notebook.get_current_page()
        for i in range(self.notebook.get_n_pages()):
            page = self.notebook.get_nth_page(i)
            label = self.notebook.get_menu_label_text(page) or ""
            name = "SwitchTab%d" % i
            tooltip = _("Switch to this tab")
            action = Gtk.RadioAction(name=name, label=label, tooltip=tooltip, stock_id=None, value=i)
            action.join_group(group)
            group = action
            action.set_active(current_page == i)

            def current_tab_changed_cb(action, current):
                if action == current:
                    self.notebook.set_current_page(action.get_current_value())
            action.connect("changed", current_tab_changed_cb)
            if i < 10:
                accel = "<Alt>%d" % ((i + 1) % 10)
            else:
                accel = None
            self.tab_switch_actiongroup.add_action_with_accel(action, accel)
            self.ui.add_ui(self.tab_switch_merge_id,
                           "/Menubar/TabMenu/TabPlaceholder",
                           name, name, Gtk.UIManagerItemType.MENUITEM, False)
Ejemplo n.º 28
0
 def get_commits_to_push_summary(self):
     branch_refs = self.get_commits_to_push()
     unpushed_branches = len([v for v in branch_refs.values() if v])
     unpushed_commits = sum(len(v) for v in branch_refs.values())
     if unpushed_commits:
         if unpushed_branches > 1:
             # Translators: First element is replaced by translated "%d
             # unpushed commits", second element is replaced by translated
             # "%d branches"
             label = _("{unpushed_commits} in {unpushed_branches}").format(
                 unpushed_commits=ngettext(
                     "%d unpushed commit", "%d unpushed commits",
                     unpushed_commits) % unpushed_commits,
                 unpushed_branches=ngettext("%d branch", "%d branches",
                                            unpushed_branches) %
                 unpushed_branches,
             )
         else:
             # Translators: These messages cover the case where there is
             # only one branch, and are not part of another message.
             label = ngettext("%d unpushed commit", "%d unpushed commits",
                              unpushed_commits) % (unpushed_commits)
     else:
         label = ""
     return label
Ejemplo n.º 29
0
Archivo: svn.py Proyecto: uestclx/meld
    def _update_tree_state_cache(self, path):
        while 1:
            try:
                proc = _vc.popen(
                    [self.CMD, "status", "-v", "--xml", path],
                    cwd=self.location)
                tree = ElementTree.parse(proc)
                break
            except OSError as e:
                if e.errno != errno.EAGAIN:
                    raise

        for target in tree.findall("target") + tree.findall("changelist"):
            for entry in (t for t in target.getchildren() if t.tag == "entry"):
                path = entry.attrib["path"]
                if not path:
                    continue
                if not os.path.isabs(path):
                    path = os.path.abspath(os.path.join(self.location, path))
                for status in (e for e in entry.getchildren() \
                               if e.tag == "wc-status"):
                    item = status.attrib["item"]
                    if item == "":
                        continue
                    state = self.state_map.get(item, _vc.STATE_NONE)
                    self._tree_cache[path] = state

                    rev = status.attrib.get("revision")
                    rev_label = _("Rev %s") % rev if rev is not None else ''
                    self._tree_meta_cache[path] = rev_label
Ejemplo n.º 30
0
 def append_diff(
     self,
     gfiles: Sequence[Optional[Gio.File]],
     auto_compare: bool = False,
     auto_merge: bool = False,
     merge_output: Optional[Gio.File] = None,
     meta: Optional[Dict[str, Any]] = None,
 ):
     have_directories = False
     have_files = False
     for f in gfiles:
         if not f:
             continue
         file_type = f.query_file_type(Gio.FileQueryInfoFlags.NONE, None)
         if file_type == Gio.FileType.DIRECTORY:
             have_directories = True
         else:
             have_files = True
     if have_directories and have_files:
         raise ValueError(
             _("Cannot compare a mixture of files and directories"))
     elif have_directories:
         return self.append_dirdiff(gfiles, auto_compare)
     elif auto_merge:
         return self.append_filemerge(gfiles, merge_output=merge_output)
     else:
         return self.append_filediff(gfiles,
                                     merge_output=merge_output,
                                     meta=meta)
Ejemplo n.º 31
0
    def _update_tree_state_cache(self, path):
        while 1:
            try:
                proc = _vc.popen(
                    [self.CMD, "status", "-v", "--xml", path],
                    cwd=self.location)
                tree = ElementTree.parse(proc)
                break
            except OSError as e:
                if e.errno != errno.EAGAIN:
                    raise

        for target in tree.findall("target") + tree.findall("changelist"):
            for entry in (t for t in target.getchildren() if t.tag == "entry"):
                path = entry.attrib["path"]
                if not path:
                    continue
                if not os.path.isabs(path):
                    path = os.path.abspath(os.path.join(self.location, path))
                for status in (e for e in entry.getchildren()
                               if e.tag == "wc-status"):
                    item = status.attrib["item"]
                    if item == "":
                        continue
                    state = self.state_map.get(item, _vc.STATE_NONE)
                    self._tree_cache[path] = state

                    rev = status.attrib.get("revision")
                    rev_label = _("Rev %s") % rev if rev is not None else ''
                    self._tree_meta_cache[path] = rev_label
                    self._add_missing_cache_entry(path, state)
Ejemplo n.º 32
0
    def run(self):
        self.update_patch()

        while 1:
            result = self.widget.run()
            if result < 0:
                break

            buf = self.textview.get_buffer()
            start, end = buf.get_bounds()
            txt = text_type(buf.get_text(start, end, False), 'utf8')

            # Copy patch to clipboard
            if result == 1:
                clip = Gtk.clipboard_get()
                clip.set_text(txt)
                clip.store()
                break
            # Save patch as a file
            else:
                # FIXME: These filediff methods are actually general utility.
                filename = self.filediff._get_filename_for_saving(
                    _("Save Patch"))
                if filename:
                    txt = txt.encode('utf-8')
                    self.filediff._save_text_to_filename(filename, txt)
                    break

        self.widget.hide()
Ejemplo n.º 33
0
def shorten_names(*names):
    """Remove redunant parts of a list of names (e.g. /tmp/foo{1,2} -> foo{1,2}
    """
    # TODO: Update for different path separators
    prefix = os.path.commonprefix(names)
    prefixslash = prefix.rfind("/") + 1

    names = [n[prefixslash:] for n in names]
    paths = [n.split("/") for n in names]

    try:
        basenames = [p[-1] for p in paths]
    except IndexError:
        pass
    else:
        if all_same(basenames):
            def firstpart(alist):
                if len(alist) > 1:
                    return "[%s] " % alist[0]
                else:
                    return ""
            roots = [firstpart(p) for p in paths]
            base = basenames[0].strip()
            return [r + base for r in roots]
    # no common path. empty names get changed to "[None]"
    return [name or _("[None]") for name in basenames]
Ejemplo n.º 34
0
def colour_lookup_with_fallback(name, attribute):
    from meld.settings import meldsettings
    source_style = meldsettings.style_scheme

    style = source_style.get_style(name)
    style_attr = getattr(style.props, attribute) if style else None
    if not style or not style_attr:
        manager = GtkSource.StyleSchemeManager.get_default()
        source_style = manager.get_scheme(MELD_STYLE_SCHEME)
        try:
            style = source_style.get_style(name)
            style_attr = getattr(style.props, attribute)
        except AttributeError:
            pass

    if not style_attr:
        import sys
        print >> sys.stderr, _(
            "Couldn't find colour scheme details for %s-%s; "
            "this is a bad install") % (name, attribute)
        sys.exit(1)

    colour = Gdk.RGBA()
    colour.parse(style_attr)
    return colour
Ejemplo n.º 35
0
Archivo: git.py Proyecto: Psykar/meld
    def _update_tree_state_cache(self, path, tree_state):
        """ Update the state of the file(s) at tree_state['path'] """
        while 1:
            try:
                entries = self._get_modified_files(path)

                # Identify ignored files and folders
                proc = _vc.popen([self.CMD, "ls-files", "--others",
                                  "--ignored", "--exclude-standard",
                                  "--directory", path],
                                 cwd=self.location)
                ignored_entries = proc.read().split("\n")[:-1]

                # Identify unversioned files
                proc = _vc.popen([self.CMD, "ls-files", "--others",
                                  "--exclude-standard", path],
                                 cwd=self.location)
                unversioned_entries = proc.read().split("\n")[:-1]

                break
            except OSError as e:
                if e.errno != errno.EAGAIN:
                    raise

        if len(entries) == 0 and os.path.isfile(path):
            # If we're just updating a single file there's a chance that it
            # was it was previously modified, and now has been edited
            # so that it is un-modified.  This will result in an empty
            # 'entries' list, and tree_state['path'] will still contain stale
            # data.  When this corner case occurs we force tree_state['path']
            # to STATE_NORMAL.
            path = os.path.abspath(path)
            tree_state[path] = _vc.STATE_NORMAL
        else:
            # There are 1 or more modified files, parse their state
            for entry in entries:
                columns = self.DIFF_RE.search(entry).groups()
                old_mode, new_mode, statekey, name = columns
                if os.name == 'nt':
                    # Git returns unix-style paths on Windows
                    name = os.path.normpath(name.strip())
                path = os.path.join(self.root, name.strip())
                path = os.path.abspath(path)
                state = self.state_map.get(statekey.strip(), _vc.STATE_NONE)
                tree_state[path] = state
                if old_mode != new_mode:
                    msg = _("Mode changed from %s to %s" %
                            (old_mode, new_mode))
                    self._tree_meta_cache[path] = msg

            for entry in ignored_entries:
                path = os.path.join(self.location, entry.strip())
                path = os.path.abspath(path)
                tree_state[path] = _vc.STATE_IGNORED

            for entry in unversioned_entries:
                path = os.path.join(self.location, entry.strip())
                path = os.path.abspath(path)
                tree_state[path] = _vc.STATE_NONE
Ejemplo n.º 36
0
Archivo: git.py Proyecto: uestclx/meld
    def _update_tree_state_cache(self, path):
        """ Update the state of the file(s) at self._tree_cache['path'] """
        while 1:
            try:
                entries = self._get_modified_files(path)

                # Identify ignored files and folders
                proc = self.run(
                    "ls-files", "--others", "--ignored", "--exclude-standard",
                    "--directory", path)
                ignored_entries = proc.stdout.read().split("\n")[:-1]

                # Identify unversioned files
                proc = self.run(
                    "ls-files", "--others", "--exclude-standard", path)
                unversioned_entries = proc.stdout.read().split("\n")[:-1]

                break
            except OSError as e:
                if e.errno != errno.EAGAIN:
                    raise

        def get_real_path(name):
            name = name.strip()
            if os.name == 'nt':
                # Git returns unix-style paths on Windows
                name = os.path.normpath(name)

            # Unicode file names and file names containing quotes are
            # returned by git as quoted strings
            if name[0] == '"':
                name = name[1:-1].decode('string_escape')
            return os.path.abspath(
                os.path.join(self.location, name))

        if len(entries) == 0 and os.path.isfile(path):
            # If we're just updating a single file there's a chance that it
            # was it was previously modified, and now has been edited so that
            # it is un-modified.  This will result in an empty 'entries' list,
            # and self._tree_cache['path'] will still contain stale data.
            # When this corner case occurs we force self._tree_cache['path']
            # to STATE_NORMAL.
            self._tree_cache[get_real_path(path)] = _vc.STATE_NORMAL
        else:
            for entry in entries:
                columns = self.DIFF_RE.search(entry).groups()
                old_mode, new_mode, statekey, path = columns
                state = self.state_map.get(statekey.strip(), _vc.STATE_NONE)
                self._tree_cache[get_real_path(path)] = state
                if old_mode != new_mode:
                    msg = _("Mode changed from %s to %s" %
                            (old_mode, new_mode))
                    self._tree_meta_cache[path] = msg

            for path in ignored_entries:
                self._tree_cache[get_real_path(path)] = _vc.STATE_IGNORED

            for path in unversioned_entries:
                self._tree_cache[get_real_path(path)] = _vc.STATE_NONE
Ejemplo n.º 37
0
class LabeledObjectMixin(GObject.GObject):

    label_text = _("untitled")
    tooltip_text = None

    @GObject.Signal
    def label_changed(self, label_text: str, tooltip_text: str) -> None:
        ...
Ejemplo n.º 38
0
Archivo: vcview.py Proyecto: zbyna/meld
 def on_consoleview_populate_popup(self, textview, menu):
     buf = textview.get_buffer()
     clear_cb = lambda *args: buf.delete(*buf.get_bounds())
     clear_action = Gtk.MenuItem.new_with_label(_("Clear"))
     clear_action.connect("activate", clear_cb)
     menu.insert(clear_action, 0)
     menu.insert(Gtk.SeparatorMenuItem(), 1)
     menu.show_all()
Ejemplo n.º 39
0
Archivo: vcview.py Proyecto: thics/meld
 def on_consoleview_populate_popup(self, textview, menu):
     buf = textview.get_buffer()
     clear_cb = lambda *args: buf.delete(*buf.get_bounds())
     clear_action = Gtk.MenuItem.new_with_label(_("Clear"))
     clear_action.connect("activate", clear_cb)
     menu.insert(clear_action, 0)
     menu.insert(Gtk.SeparatorMenuItem(), 1)
     menu.show_all()
Ejemplo n.º 40
0
    def run_diff(self, path):
        if os.path.isdir(path):
            self.emit("create-diff", [path], {})
            return

        left_is_local = self.props.left_is_local
        basename = self.display_path(os.path.basename(path))
        meta = {
            'parent': self,
            'prompt_resolve': False,
        }

        # May have removed directories in list.
        vc_entry = self.vc.get_entry(path)
        if vc_entry and vc_entry.state == tree.STATE_CONFLICT and \
                hasattr(self.vc, 'get_path_for_conflict'):
            local_label = _(u"%s — local") % basename
            remote_label = _(u"%s — remote") % basename

            # We create new temp files for other, base and this, and
            # then set the output to the current file.
            if self.props.merge_file_order == "local-merge-remote":
                conflicts = (tree.CONFLICT_THIS, tree.CONFLICT_MERGED,
                             tree.CONFLICT_OTHER)
                meta['labels'] = (local_label, None, remote_label)
                meta['tablabel'] = _(u"%s (local, merge, remote)") % basename
            else:
                conflicts = (tree.CONFLICT_OTHER, tree.CONFLICT_MERGED,
                             tree.CONFLICT_THIS)
                meta['labels'] = (remote_label, None, local_label)
                meta['tablabel'] = _(u"%s (remote, merge, local)") % basename
            diffs = [self.vc.get_path_for_conflict(path, conflict=c)
                     for c in conflicts]
            temps = [p for p, is_temp in diffs if is_temp]
            diffs = [p for p, is_temp in diffs]
            kwargs = {
                'auto_merge': False,
                'merge_output': path,
            }
            meta['prompt_resolve'] = True
        else:
            remote_label = _(u"%s — repository") % basename
            comp_path = self.vc.get_path_for_repo_file(path)
            temps = [comp_path]
            if left_is_local:
                diffs = [path, comp_path]
                meta['labels'] = (None, remote_label)
                meta['tablabel'] = _(u"%s (working, repository)") % basename
            else:
                diffs = [comp_path, path]
                meta['labels'] = (remote_label, None)
                meta['tablabel'] = _(u"%s (repository, working)") % basename
            kwargs = {}
        kwargs['meta'] = meta

        for temp_file in temps:
            os.chmod(temp_file, 0o444)
            _temp_files.append(temp_file)

        self.emit("create-diff", diffs, kwargs)
Ejemplo n.º 41
0
    def setup_integration(self):
        if self.is_alias_found():
            add_shortcut = modal_dialog(
                primary=_("Mac Shell Integration already exists"),
                secondary=_("Overwrite alias for meld?" "\n\n*Note*: alias already exists "),
                buttons=[
                    (_("_Cancel"), Gtk.ResponseType.CANCEL),
                    (_("Overwrite"), Gtk.ResponseType.OK),
                ],
                messagetype=Gtk.MessageType.QUESTION
            )
        else:
            add_shortcut = Gtk.ResponseType.OK

        if add_shortcut == Gtk.ResponseType.OK:
            try: 
                self.create_shell_alias()
                modal_dialog(
                    primary=_(
                        "Alias created"
                    ),
                    secondary=_(
                        "You should be able to use meld from the command line.\n\n"
                        "New Terminals will work automatically. For Terminals that are already open, issue the command:\n\n"
                        "source ~/.bashrc"
                    ),
                    buttons=[
                        (_("OK"), Gtk.ResponseType.OK),
                    ],
                    messagetype=Gtk.MessageType.INFO
                )
            except:
                modal_dialog(
                    primary=_(
                        "Failed to create/update alias"
                    ),
                    secondary=_(
                        "Meld was unable to create the alias required for shell operation. "
                        "Edit your ~/.bashrc and add the line: alias meld={}".format(self.executable_path)
                    ),
                    buttons=[
                        (_("OK"), Gtk.ResponseType.OK),
                    ],
                    messagetype=Gtk.MessageType.WARNING
                )
Ejemplo n.º 42
0
Archivo: vcview.py Proyecto: fy2/meld
    def run_diff(self, path):
        if os.path.isdir(path):
            self.emit("create-diff", [path], {})
            return

        left_is_local = self.props.left_is_local
        basename = os.path.basename(path)
        meta = {
            'parent': self,
            'prompt_resolve': False,
        }

        # May have removed directories in list.
        vc_entry = self.vc.get_entry(path)
        if vc_entry and vc_entry.state == tree.STATE_CONFLICT and \
                hasattr(self.vc, 'get_path_for_conflict'):
            local_label = _(u"%s — local") % basename
            remote_label = _(u"%s — remote") % basename

            # We create new temp files for other, base and this, and
            # then set the output to the current file.
            if self.props.merge_file_order == "local-merge-remote":
                conflicts = (tree.CONFLICT_THIS, tree.CONFLICT_MERGED,
                             tree.CONFLICT_OTHER)
                meta['labels'] = (local_label, None, remote_label)
                meta['tablabel'] = _(u"%s (local, merge, remote)") % basename
            else:
                conflicts = (tree.CONFLICT_OTHER, tree.CONFLICT_MERGED,
                             tree.CONFLICT_THIS)
                meta['labels'] = (remote_label, None, local_label)
                meta['tablabel'] = _(u"%s (remote, merge, local)") % basename
            diffs = [self.vc.get_path_for_conflict(path, conflict=c)
                     for c in conflicts]
            temps = [p for p, is_temp in diffs if is_temp]
            diffs = [p for p, is_temp in diffs]
            kwargs = {
                'auto_merge': False,
                'merge_output': path,
            }
            meta['prompt_resolve'] = True
        else:
            remote_label = _(u"%s — repository") % basename
            comp_path = self.vc.get_path_for_repo_file(path)
            temps = [comp_path]
            if left_is_local:
                diffs = [path, comp_path]
                meta['labels'] = (None, remote_label)
                meta['tablabel'] = _(u"%s (working, repository)") % basename
            else:
                diffs = [comp_path, path]
                meta['labels'] = (remote_label, None)
                meta['tablabel'] = _(u"%s (repository, working)") % basename
            kwargs = {}
        kwargs['meta'] = meta

        for temp_file in temps:
            os.chmod(temp_file, 0o444)
            _temp_files.append(temp_file)

        self.emit("create-diff", diffs, kwargs)
Ejemplo n.º 43
0
 def append_filemerge(self, files, merge_output=None):
     if len(files) != 3:
         raise ValueError(_("Need three files to auto-merge, got: %r") % files)
     doc = filemerge.FileMerge(len(files))
     self._append_page(doc, "text-x-generic")
     doc.set_files(files)
     if merge_output is not None:
         doc.set_merge_output_file(merge_output)
     return doc
Ejemplo n.º 44
0
    def _make_copy_menu(self, chunk):
        copy_menu = Gtk.Menu()
        copy_up = Gtk.MenuItem.new_with_mnemonic(_("Copy _up"))
        copy_down = Gtk.MenuItem.new_with_mnemonic(_("Copy _down"))
        copy_menu.append(copy_up)
        copy_menu.append(copy_down)
        copy_menu.show_all()

        # FIXME: This is horrible
        copy_menu.attach_to_widget(self.filediff, None)

        def copy_chunk(widget, chunk, copy_up):
            self.filediff.copy_chunk(self.from_pane, self.to_pane, chunk,
                                     copy_up)

        copy_up.connect('activate', copy_chunk, chunk, True)
        copy_down.connect('activate', copy_chunk, chunk, False)
        return copy_menu
Ejemplo n.º 45
0
def prompt_save_filename(
        title: str, parent: Optional[Gtk.Widget] = None) -> Optional[Gio.File]:

    dialog = MeldFileChooserDialog(
        title,
        transient_for=get_modal_parent(parent),
        action=Gtk.FileChooserAction.SAVE,
    )
    dialog.set_default_response(Gtk.ResponseType.ACCEPT)
    response = dialog.run()
    gfile = dialog.get_file()
    dialog.destroy()

    if response != Gtk.ResponseType.ACCEPT or not gfile:
        return None

    try:
        file_info = gfile.query_info(
            'standard::name,standard::display-name', 0, None)
    except GLib.Error as err:
        if err.code == Gio.IOErrorEnum.NOT_FOUND:
            return gfile
        raise

    # The selected file exists, so we need to prompt for overwrite.
    parent_name = gfile.get_parent().get_parse_name()
    file_name = file_info.get_display_name()

    replace = modal_dialog(
        primary=_("Replace file “%s”?") % file_name,
        secondary=_(
            "A file with this name already exists in “%s”.\n"
            "If you replace the existing file, its contents "
            "will be lost.") % parent_name,
        buttons=[
            (_("_Cancel"), Gtk.ResponseType.CANCEL),
            (_("_Replace"), Gtk.ResponseType.OK),
        ],
        messagetype=Gtk.MessageType.WARNING,
    )
    if replace != Gtk.ResponseType.OK:
        return None

    return gfile
Ejemplo n.º 46
0
 def on_button_delete_clicked(self, obj):
     files = self._get_selected_files()
     for name in files:
         try:
             gfile = Gio.File.new_for_path(name)
             gfile.trash(None)
         except GLib.GError as e:
             misc.error_dialog(_("Error removing %s") % name, str(e))
     workdir = _commonprefix(files)
     self.refresh_partial(workdir)
Ejemplo n.º 47
0
    def _make_copy_menu(self, chunk):
        copy_menu = Gtk.Menu()
        copy_up = Gtk.MenuItem.new_with_mnemonic(_("Copy _up"))
        copy_down = Gtk.MenuItem.new_with_mnemonic(_("Copy _down"))
        copy_menu.append(copy_up)
        copy_menu.append(copy_down)
        copy_menu.show_all()

        # FIXME: This is horrible
        widget = self.filediff.widget
        copy_menu.attach_to_widget(widget, None)

        def copy_chunk(widget, chunk, copy_up):
            self.filediff.copy_chunk(self.from_pane, self.to_pane, chunk,
                                     copy_up)

        copy_up.connect('activate', copy_chunk, chunk, True)
        copy_down.connect('activate', copy_chunk, chunk, False)
        return copy_menu
Ejemplo n.º 48
0
Archivo: vcview.py Proyecto: fy2/meld
 def on_button_delete_clicked(self, obj):
     files = self._get_selected_files()
     for name in files:
         try:
             gfile = Gio.File.new_for_path(name)
             gfile.trash(None)
         except GLib.GError as e:
             misc.error_dialog(_("Error removing %s") % name, str(e))
     workdir = _commonprefix(files)
     self.refresh_partial(workdir)
Ejemplo n.º 49
0
 def append_filemerge(self, files, merge_output=None):
     if len(files) != 3:
         raise ValueError(
             _("Need three files to auto-merge, got: %r") % files)
     doc = filemerge.FileMerge(len(files))
     self._append_page(doc, "text-x-generic")
     doc.set_files(files)
     if merge_output is not None:
         doc.set_merge_output_file(merge_output)
     return doc
Ejemplo n.º 50
0
 def _find_text(self, start_offset=1, backwards=False, wrap=True):
     match_case = self.match_case.get_active()
     whole_word = self.whole_word.get_active()
     regex = self.regex.get_active()
     assert self.textview
     buf = self.textview.get_buffer()
     insert = buf.get_iter_at_mark(buf.get_insert())
     tofind_utf8 = self.find_entry.get_text()
     tofind = tofind_utf8.decode("utf-8")
     start, end = buf.get_bounds()
     text = buf.get_text(start, end, False).decode("utf-8")
     if not regex:
         tofind = re.escape(tofind)
     if whole_word:
         tofind = r'\b' + tofind + r'\b'
     try:
         flags = re.M if match_case else re.M | re.I
         pattern = re.compile(tofind, flags)
     except re.error as e:
         misc.error_dialog(_("Regular expression error"), str(e))
     else:
         self.wrap_box.set_visible(False)
         if not backwards:
             match = pattern.search(text,
                                    insert.get_offset() + start_offset)
             if match is None and wrap:
                 self.wrap_box.set_visible(True)
                 match = pattern.search(text, 0)
         else:
             match = None
             for m in pattern.finditer(text, 0, insert.get_offset()):
                 match = m
             if match is None and wrap:
                 self.wrap_box.set_visible(True)
                 for m in pattern.finditer(text, insert.get_offset()):
                     match = m
         if match:
             it = buf.get_iter_at_offset(match.start())
             buf.place_cursor(it)
             it.forward_chars(match.end() - match.start())
             buf.move_mark(buf.get_selection_bound(), it)
             self.textview.scroll_to_mark(
                 buf.get_insert(), 0.25, True, 0.5, 0.5)
             return True
         else:
             buf.place_cursor(buf.get_iter_at_mark(buf.get_insert()))
             # FIXME: Even though docs suggest this should work, it does
             # not. It just sets the selection colour on the text, without
             # affecting the entry colour at all.
             color = Gdk.RGBA()
             color.parse("#ffdddd")
             self.find_entry.override_background_color(
                 Gtk.StateType.NORMAL, color)
             self.wrap_box.set_visible(False)
Ejemplo n.º 51
0
class LabeledObjectMixin(GObject.GObject):
    __gsignals__ = {
        'label-changed': (GObject.SignalFlags.RUN_FIRST, None,
                          (GObject.TYPE_STRING, GObject.TYPE_STRING)),
    }

    label_text = _("untitled")
    tooltip_text = None

    def label_changed(self):
        self.emit("label-changed", self.label_text, self.tooltip_text)
Ejemplo n.º 52
0
        def make_file_from_command_line(arg):
            f = command_line.create_file_for_arg(arg)
            if not f.query_exists(cancellable=None):
                # May be a relative path with ':', misinterpreted as a URI
                cwd = Gio.File.new_for_path(command_line.get_cwd())
                relative = Gio.File.resolve_relative_path(cwd, arg)
                if relative.query_exists(cancellable=None):
                    return relative
                # Return the original arg for a better error message

            if f.get_uri() is None:
                raise ValueError(_("invalid path or URI “%s”") % arg)

            # TODO: support for directories specified by URIs
            file_type = f.query_file_type(Gio.FileQueryInfoFlags.NONE, None)
            if not f.is_native() and file_type == Gio.FileType.DIRECTORY:
                raise ValueError(
                    _("remote folder “{}” not supported").format(arg))

            return f
Ejemplo n.º 53
0
 def append_diff(self, paths, auto_compare=False, auto_merge=False, merge_output=None, meta=None):
     dirslist = [p for p in paths if os.path.isdir(p)]
     fileslist = [p for p in paths if os.path.isfile(p)]
     if dirslist and fileslist:
         raise ValueError(_("Cannot compare a mixture of files and directories"))
     elif dirslist:
         return self.append_dirdiff(paths, auto_compare)
     elif auto_merge:
         return self.append_filemerge(paths, merge_output=merge_output)
     else:
         return self.append_filediff(paths, merge_output=merge_output, meta=meta)
Ejemplo n.º 54
0
    def __init__(self, iconname, text, onclose):
        Gtk.HBox.__init__(self, homogeneous=False, spacing=4)

        label = Gtk.Label(label=text)
        # FIXME: ideally, we would use custom ellipsization that ellipsized the
        # two paths separately, but that requires significant changes to label
        # generation in many different parts of the code
        label.set_ellipsize(Pango.EllipsizeMode.MIDDLE)
        label.set_single_line_mode(True)
        label.set_alignment(0.0, 0.5)
        label.set_padding(0, 0)

        context = self.get_pango_context()
        font_desc = self.get_style_context().get_font(Gtk.StateFlags.NORMAL)
        metrics = context.get_metrics(font_desc, context.get_language())
        char_width = metrics.get_approximate_char_width() / Pango.SCALE
        valid, w, h = Gtk.icon_size_lookup_for_settings(
            self.get_settings(), Gtk.IconSize.MENU)
        # FIXME: PIXELS replacement
        self.set_size_request(
            self.tab_width_in_chars * char_width + 2 * w, -1)

        button = Gtk.Button()
        button.set_relief(Gtk.ReliefStyle.NONE)
        button.set_focus_on_click(False)
        icon = Gio.ThemedIcon.new_with_default_fallbacks(
            'window-close-symbolic')
        image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.MENU)
        image.set_tooltip_text(_("Close tab"))
        button.add(image)
        button.set_name("meld-tab-close-button")
        button.connect("clicked", onclose)

        context = button.get_style_context()
        provider = Gtk.CssProvider()
        provider.load_from_data(self.css)
        context.add_provider(provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)

        icon = Gtk.Image.new_from_icon_name(iconname, Gtk.IconSize.MENU)

        label_box = Gtk.EventBox()
        label_box.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)
        label_box.props.visible_window = False
        label_box.connect("button-press-event", self.on_label_clicked)
        label_box.add(label)

        self.pack_start(icon, False, True, 0)
        self.pack_start(label_box, True, True, 0)
        self.pack_start(button, False, True, 0)
        self.set_tooltip_text(text)
        self.show_all()

        self.__label = label
        self.__onclose = onclose
Ejemplo n.º 55
0
 def update_filename_filters(self, *args):
     filter_items_model = Gio.Menu()
     for i, filt in enumerate(meldsettings.file_filters):
         name = FILE_FILTER_ACTION_FORMAT.format(i)
         filter_items_model.append(
             label=filt.label, detailed_action=f'view.{name}')
     section = Gio.MenuItem.new_section(_("Filename"), filter_items_model)
     section.set_attribute([("id", "s", "custom-filter-section")])
     app = self.get_application()
     filter_model = app.get_menu_by_id("folder-status-filter-menu")
     replace_menu_section(filter_model, section)
Ejemplo n.º 56
0
        def make_file_from_command_line(arg):
            f = command_line.create_file_for_arg(arg)
            if not f.query_exists(cancellable=None):
                # May be a relative path with ':', misinterpreted as a URI
                cwd = Gio.File.new_for_path(command_line.get_cwd())
                relative = Gio.File.resolve_relative_path(cwd, arg)
                if relative.query_exists(cancellable=None):
                    return relative
                # Return the original arg for a better error message

            if f.get_uri() is None:
                raise ValueError(_("invalid path or URI “%s”") % arg)

            # TODO: support for directories specified by URIs
            file_type = f.query_file_type(Gio.FileQueryInfoFlags.NONE, None)
            if not f.is_native() and file_type == Gio.FileType.DIRECTORY:
                raise ValueError(
                    _("remote folder “{}” not supported").format(arg))

            return f
Ejemplo n.º 57
0
 def append_filemerge(self, gfiles, merge_output=None):
     if len(gfiles) != 3:
         raise ValueError(
             _("Need three files to auto-merge, got: %r") %
             [f.get_parse_name() for f in gfiles])
     doc = FileMerge(len(gfiles))
     self._append_page(doc, "text-x-generic")
     doc.set_files(gfiles)
     if merge_output is not None:
         doc.set_merge_output_file(merge_output)
     return doc
Ejemplo n.º 58
0
 def _merge_files(self):
     yield _("[%s] Merging files") % self.label_text
     merger = merge.Merger()
     step = merger.initialize(self.buffer_filtered, self.buffer_texts)
     while next(step) is None:
         yield 1
     for merged_text in merger.merge_3_files():
         yield 1
     self.linediffer.unresolved = merger.unresolved
     self.textbuffer[1].set_text(merged_text)
     self.recompute_label()
Ejemplo n.º 59
0
    def append_new_comparison(self):
        doc = NewDiffTab(self)
        self._append_page(doc, "document-new")
        self.notebook.on_label_changed(doc, _("New comparison"), None)

        def diff_created_cb(doc, newdoc):
            doc.on_delete_event()
            idx = self.notebook.page_num(newdoc)
            self.notebook.set_current_page(idx)

        doc.connect("diff-created", diff_created_cb)
        return doc
Ejemplo n.º 60
0
    def add_action_msg(self, icon, primary, secondary, action_label, callback):
        def on_response(msgarea, response_id, *args):
            self.clear()
            if response_id == Gtk.ResponseType.ACCEPT:
                callback()

        msgarea = self.new_from_text_and_icon(icon, primary, secondary)
        msgarea.add_button(action_label, Gtk.ResponseType.ACCEPT)
        msgarea.add_button(_("Hi_de"), Gtk.ResponseType.CLOSE)
        msgarea.connect("response", on_response)
        msgarea.show_all()
        return msgarea