From 66c20272bc597dd13daaa9345129f625f2598c1c Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Wed, 3 Nov 2021 12:25:48 -0400 Subject: [PATCH] import gedit-40.0-6.el9 --- .gedit.metadata | 1 + .gitignore | 1 + ...001-Add-40.0-release-info-to-appdata.patch | 24 + ...0001-deteplification-the-mega-commit.patch | 20233 ++++++++++++++++ SOURCES/python3-install-path-fix.patch | 11 + SOURCES/restore-overlay-scrollbars.patch | 26 + SPECS/gedit.spec | 1513 ++ 7 files changed, 21809 insertions(+) create mode 100644 .gedit.metadata create mode 100644 .gitignore create mode 100644 SOURCES/0001-Add-40.0-release-info-to-appdata.patch create mode 100644 SOURCES/0001-deteplification-the-mega-commit.patch create mode 100644 SOURCES/python3-install-path-fix.patch create mode 100644 SOURCES/restore-overlay-scrollbars.patch create mode 100644 SPECS/gedit.spec diff --git a/.gedit.metadata b/.gedit.metadata new file mode 100644 index 0000000..4959309 --- /dev/null +++ b/.gedit.metadata @@ -0,0 +1 @@ +8bd1e4e6b5f4d8761bdbb90ea13bc5d1e26c625e SOURCES/gedit-40.0.tar.xz diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..082979b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/gedit-40.0.tar.xz diff --git a/SOURCES/0001-Add-40.0-release-info-to-appdata.patch b/SOURCES/0001-Add-40.0-release-info-to-appdata.patch new file mode 100644 index 0000000..a9f5961 --- /dev/null +++ b/SOURCES/0001-Add-40.0-release-info-to-appdata.patch @@ -0,0 +1,24 @@ +From a03d70b6fb886e7c7e44775562826377fe073c27 Mon Sep 17 00:00:00 2001 +From: Kalev Lember +Date: Fri, 2 Apr 2021 13:45:37 +0200 +Subject: [PATCH] Add 40.0 release info to appdata + +--- + data/org.gnome.gedit.appdata.xml.in | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/data/org.gnome.gedit.appdata.xml.in b/data/org.gnome.gedit.appdata.xml.in +index 5bf14503c..c5d3bdc94 100644 +--- a/data/org.gnome.gedit.appdata.xml.in ++++ b/data/org.gnome.gedit.appdata.xml.in +@@ -36,6 +36,7 @@ + gedit + + ++ + + + +-- +2.30.2 + diff --git a/SOURCES/0001-deteplification-the-mega-commit.patch b/SOURCES/0001-deteplification-the-mega-commit.patch new file mode 100644 index 0000000..dda15b4 --- /dev/null +++ b/SOURCES/0001-deteplification-the-mega-commit.patch @@ -0,0 +1,20233 @@ +From b190bdb716fc8bb0cd515ee10fb3c51debede5fa Mon Sep 17 00:00:00 2001 +From: Zander Brown +Date: Tue, 13 Apr 2021 04:53:21 +0000 +Subject: [PATCH] deteplification: the mega commit +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This commit is a reverse patch of a rebased version of gedit with +commits related to tepl/amtk removed + +Sébastien Wilmet has stopped development of these libraries, relevant +commits: + +https://gitlab.gnome.org/GNOME/amtk/-/commit/34a1171298808e5d9bd50540ee194b8be35bce9d +https://gitlab.gnome.org/GNOME/tepl/-/commit/457b5c37ebcb2ebb23cae10cd47f1342db45a98e + +As a result usage is being dropped in favour simplifing gedit +dependencies, because there are not enough developers, and too many bugs. +Do you prefer a rock-solid text editor? Or a text editor with some shiny +and non-essential dependencies that complicate the build, at the expense +of more bugs? + +Tracked at: https://gitlab.gnome.org/Infrastructure/Infrastructure/-/issues/564 + +Commits Dropped: +cfeb7cb6c build: add Tepl dependency, second try +872fb4809 Utils: use str truncate functions from Tepl +9a121d523 Utils: deprecate str_end_truncate() +e0e602799 No longer use gedit_utils_str_end_truncate() +657ce9f7a Utils: deprecate str_middle_truncate() +6eefa74c2 No longer use gedit_utils_str_middle_truncate() +169c1594e main: call tepl_init() and tepl_finalize() +ec31f0c22 snap: Fixed build failures by added tepl part +76cf5562d Document: remove metadata implementation +75cdb6a77 Document: second pass to remove metadata implementation +d6b158ca6 App: remove the GeditMetadataManager +6eccd6672 Remove GeditMetadataManager +03929157e build: remove enable-gvfs-metadata option +1dad3bce2 build: remove libxml dependency +8c0825424 metadata: adapt key names for TeplFileMetadata +c2904aa80 Document: metadata: create an internal TeplFile +9c8ede1e2 App: setup Tepl metadata manager +91bc04efc App: remove leftover comment +b39dd0201 Document: use TeplFileMetadata to re-implement set/get_metadata() +494677bfc Utils: use tepl_utils_replace_home_dir_with_tilde() +aaaa28a87 docs: gtk-doc fixxref: fix the path for gtk and add path for tepl +8710e89d8 Remove GeditViewCentering +5e0909c19 ViewFrame: disable overlay scrolling for the GtkScrolledWindow +7c7296b0e Remove overview map: remove from preferences dialog +26899e0ba Remove overview map +27f54a9ae replace-open-button: remove OpenDocumentSelector +504ced5a9 replace-open-button: Window: remove open document popover +3a8c5bbc2 replace-open-button: remove open button UI +abab6529f replace-open-button: re-create a simple Open button +1ee984a41 replace-open-button: re-create simple Open Recent menu button (UI) +f1a58a70b replace-open-button: set Open Recent menu with Amtk +020e0b340 replace-open-button: re-handle the fullscreen mode +4dfd2104e Window: remove dead code (#defines) +0caf371b1 Window: make the code a little clearer wrt fullscreen mode +584aeb202 Window: rename fullscreen_controls -> fullscreen_revealer +75705bf1d Window: some code cleanup wrt fullscreen mode +cf0d62617 Window: simplify setting the state of the fullscreen revealer +8706e5624 Window: fullscreen mode: remove idle function +6361b281c Window: fullscreen mode: remove no longer needed code +8d3e6faf2 Window: fullscreen mode: fix headerbar shown/hidden "stuttering" +a437e5245 Window: fullscreen mode: add comment about the "stuttering" fix +d064c5f88 Window: remove useless #include +15833e5c7 build: list of deps: simplify +007caff57 build: list of deps: simplify version requirements +72dc3a477 build: avoid the use of a variable +4a49c8562 flatpak: update Amtk to 5.0.2 +9ab7b0f0a utils: remove GBOOLEAN_TO_POINTER() and GPOINTER_TO_BOOLEAN() +55c5f1423 Utils: deprecate gedit_warning() +cbdd3a85d PreferencesDialog: use tepl_utils_show_warning_dialog() +7a442c766 Utils: deprecate decode_uri(), use the tepl one +bf3d57003 Use tepl_utils_decode_uri() +2e45bb03f io-error-info-bar: remove useless #include's +092411115 io-error-info-bar: use TeplInfoBar for the file_already_open_warning +8ccb4566f io-error-info-bar: file_already_open_warning moved to Tepl +8bfe0559a io-error-info-bar: use TeplInfoBar for no_backup_saving_error +ca582d2c7 io-error-info-bar: fix fixme in no_backup_saving_error +fa1758081 io-error-info-bar: no_backup_saving_error: show also error->message +321eccdd7 io-error-info-bar: no_back_saving_error moved to Tepl +7bb880114 io-error-info-bar: tepl function renamed to have a shorter name +d1a14a063 io-error-info-bar: externally_modified: use TeplInfoBar +bf60cc1af io-error-info-bar: invalid_character: TeplInfoBar + other improvements +44cff9798 io-error-info-bar: externally_modified: remove unneeded code +873ed25f1 io-error-info-bar: externally_modified: further simplify the code +a8a4724fc io-error-info-bar: externally_modified: moved to Tepl +ec8d8c717 io-error-info-bar: invalid_character: moved to Tepl +2aae17c31 flatpak: switch Tepl to Meson +9caaddeda Window: fix RTL bug for Open buttons in the headerbar +82d8c507d View: subclass TeplView +e23569c59 View: deprecate lots of functions, use the TeplView ones +291eb9c8e commands-edit: use TeplView functions +00f8c5e63 Use tepl_view_scroll_to_cursor() +5fdeaee22 Document: deprecate goto_line() and goto_line_offset() +d87bf25ba commands-file: use tepl_view_goto_line*() +fd5500d90 Tab: use tepl_view_goto_line_offset() +3c8a1dcb8 ViewFrame: use tepl_view_goto_line*() +079f9699e pkg-config file: add Tepl as public dep +cf6281d04 docs: document that gedit is in the process of using more Tepl features +9ac62a6fd build: require Tepl 5 +3f84484d8 App: port to new Tepl metadata API +9b9fa6c77 Document: port to new Tepl metadata API +491280c04 docs: roadmap: link to new Tepl roadmap +49db2a666 PreferencesDialog: use TeplStyleSchemeChooserWidget to fix several bugs +94c3b70cb PreferencesDialog: improvements to color scheme management +53cfa1715 PreferencesDialog: fix some bugs when (un)installing color schemes +da5b9af9f snap: the tepl build is meson only now +012a07cb7 snap: use the correct option to specify parameters +325aa857c Factory: class skeleton +7b8579770 Factory: implement ::create_metadata_manager_file vfunc +d5a43cece Factory: create it in main() and set it as the TeplAbstractFactory +392545195 App: use tepl_application_handle_metadata() +8b2fa9092 Keep default buttons orientation for TeplInfoBars +efe48bbe8 flatpak: remove uchardet, Tepl no longer depends on it +5e588d978 PreferencesDialog: improve code to install extra style scheme +eada68d97 PreferencesDialog: use GtkFileChooserNative, not GeditFileChooserDialog +55fc5973c FileChooserDialog: remove hide() (now dead code) +4f5a5681b FileChooserDialog: remove add_pattern_filter() (now dead code) +105ca1ded File choosers: remove GeditFileChooserFlags +ff1e9c27c Update Romanian translation +b4a52f479 Merge branch 'master' of gitlab.gnome.org:GNOME/gedit +21d94f23b Remove all the deprecated API +4b5838b1e docs: update path to Amtk docs for the gtk-doc fixxref_args +a4cf3ac57 Use tepl_pango_font_description_to_css() +205bfc855 Window: port to TeplLanguageChooserWidget +ffedbc882 commands-view: port to TeplLanguageChooserDialog +1dc325294 Remove GeditHighlightModeSelector and GeditHighlightModeDialog +e487680b2 Use Tepl 6 (currently 5.99) +ee465ef0c Tab: port to TeplProgressInfoBar +bc992e11e Remove GeditProgressInfoBar, replaced by TeplProgressInfoBar +fa9d6aeaa docs: document the GeditProgressInfoBar removal +bb4ba5543 docs: document that all deprecated APIs have been removed +325351805 GeditDocument: subclass TeplBuffer +8b01acace GeditDocument: remove the ::cursor-moved signal +b938c50f8 Use the TeplBuffer::tepl-cursor-moved signal +6b6a6e595 GeditDocument: bind GtkSourceFile and TeplFile :location properties +f73828fe1 GeditDocument: remove unused instance variable +01d8a1577 flatpak: build amtk from git master, not from an archive +6e78a57c9 GeditDocument: use tepl_file_get_short_name() +49749c38a GeditDocument: remove untitled_number handling +844f6185a Tab: use TeplFile:short-name property notification +49c0c814a GeditDocument: remove the :shortname property +a4670d7ce help: "Untitled Document" -> "Untitled File" +4d4d5d48c Use tepl_buffer_is_untouched() +b0f67d391 GeditDocument: remove is_untouched() +85b007052 GeditDocument: use the TeplFile location in is_untitled() +4df850fb6 View: use TeplSignalGroup +ef0d0ca9b View: minor code changes +284e6c05c View: make set_font() private +6b1e782d9 View: code refactorings for set_font() +ce790b510 View: use tepl_utils_override_font() +79b3e3011 GeditSettings: remove no longer needed code +da4b3e61a GeditSettings: minor code change: removed unused function param +13033affa GeditSettings: rework fonts changes handling, add ::fonts-changed signal +878ceb988 GeditSettings: minor code change, improve get_system_font() +1e89921a3 GeditSettings: add get_selected_font() +9ef4cf7ed View: simplify the code to update the font +3a3de93bd debug: remove DEBUG_METADATA (dead code) +43925dac7 recent: use the TeplFile +2f7c4a691 recent: move some functions to gedit-recent-osx +87b1f50d4 recent-osx: add TODO comment + +Brought to you by GitLens, which honesntly has been very useful +unpicking all this and sticking it back together again +--- + NEWS | 12 + + build-aux/flatpak/org.gnome.gedit.yml | 15 +- + build-aux/snap/snapcraft.yaml | 27 +- + data/org.gnome.gedit.gschema.xml.in | 5 + + docs/gedit-development-getting-started.md | 8 +- + docs/reference/api-breaks.xml | 50 + + docs/reference/gedit-docs.xml | 1 + + docs/reference/gedit-sections.txt | 36 + + docs/reference/meson.build | 6 +- + docs/roadmap-done.md | 56 - + docs/roadmap.md | 19 +- + gedit/Gedit-3.0.metadata | 1 + + gedit/gedit-app-osx.m | 4 +- + gedit/gedit-app-private.h | 3 + + gedit/gedit-app.c | 54 +- + gedit/gedit-commands-edit.c | 28 +- + gedit/gedit-commands-file.c | 26 +- + gedit/gedit-commands-search.c | 7 +- + gedit/gedit-commands-view.c | 42 +- + gedit/gedit-debug.c | 4 + + gedit/gedit-debug.h | 2 + + gedit/gedit-document-private.h | 12 +- + gedit/gedit-document.c | 534 ++++++- + gedit/gedit-document.h | 18 +- + gedit/gedit-documents-panel.c | 3 +- + gedit/gedit-factory.c | 50 - + gedit/gedit-factory.h | 53 - + gedit/gedit-file-chooser-dialog-gtk.c | 72 +- + gedit/gedit-file-chooser-dialog-gtk.h | 9 +- + gedit/gedit-file-chooser-dialog.c | 40 +- + gedit/gedit-file-chooser-dialog.h | 17 + + gedit/gedit-highlight-mode-dialog.c | 102 ++ + gedit/gedit-highlight-mode-dialog.h | 41 + + gedit/gedit-highlight-mode-selector.c | 375 +++++ + gedit/gedit-highlight-mode-selector.h | 44 + + gedit/gedit-io-error-info-bar.c | 362 ++++- + gedit/gedit-io-error-info-bar.h | 10 + + gedit/gedit-metadata-manager.c | 650 ++++++++ + gedit/gedit-metadata-manager.h | 49 + + gedit/gedit-open-document-selector-helper.c | 103 ++ + gedit/gedit-open-document-selector-helper.h | 103 ++ + gedit/gedit-open-document-selector-store.c | 820 +++++++++++ + gedit/gedit-open-document-selector-store.h | 68 + + gedit/gedit-open-document-selector.c | 1304 +++++++++++++++++ + gedit/gedit-open-document-selector.h | 44 + + gedit/gedit-pango.c | 230 +++ + gedit/gedit-pango.h | 28 + + gedit/gedit-preferences-dialog.c | 447 +++--- + gedit/gedit-print-job.c | 4 +- + gedit/gedit-progress-info-bar.c | 177 +++ + gedit/gedit-progress-info-bar.h | 53 + + gedit/gedit-recent-osx.c | 249 ---- + gedit/gedit-recent-osx.h | 54 - + gedit/gedit-recent.c | 231 ++- + gedit/gedit-recent.h | 23 +- + gedit/gedit-settings.c | 177 ++- + gedit/gedit-settings.h | 7 +- + gedit/gedit-tab.c | 115 +- + gedit/gedit-utils.c | 333 ++++- + gedit/gedit-utils.h | 21 +- + gedit/gedit-view-centering.c | 495 +++++++ + gedit/gedit-view-centering.h | 67 + + gedit/gedit-view-frame.c | 37 +- + gedit/gedit-view-frame.h | 4 + + gedit/gedit-view.c | 322 +++- + gedit/gedit-view.h | 24 +- + gedit/gedit-window-private.h | 15 +- + gedit/gedit-window.c | 380 ++--- + gedit/gedit.c | 8 +- + gedit/meson.build | 23 +- + gedit/resources/css/gedit-style.css | 12 + + gedit/resources/css/gedit.adwaita.css | 25 + + gedit/resources/gedit.gresource.xml.in | 4 + + .../ui/gedit-highlight-mode-dialog.ui | 87 ++ + .../ui/gedit-highlight-mode-selector.ui | 83 ++ + .../ui/gedit-open-document-selector.ui | 115 ++ + .../resources/ui/gedit-preferences-dialog.ui | 30 +- + gedit/resources/ui/gedit-progress-info-bar.ui | 88 ++ + gedit/resources/ui/gedit-view-frame.ui | 27 +- + gedit/resources/ui/gedit-window.ui | 87 +- + help/C/gedit-tab-groups.page | 4 +- + meson.build | 34 +- + meson_options.txt | 7 + + plugins/snippets/snippets/document.py | 10 +- + plugins/spell/gedit-spell-plugin.c | 9 +- + po/POTFILES.in | 8 +- + 86 files changed, 8160 insertions(+), 1283 deletions(-) + delete mode 100644 docs/roadmap-done.md + delete mode 100644 gedit/gedit-factory.c + delete mode 100644 gedit/gedit-factory.h + create mode 100644 gedit/gedit-highlight-mode-dialog.c + create mode 100644 gedit/gedit-highlight-mode-dialog.h + create mode 100644 gedit/gedit-highlight-mode-selector.c + create mode 100644 gedit/gedit-highlight-mode-selector.h + create mode 100644 gedit/gedit-metadata-manager.c + create mode 100644 gedit/gedit-metadata-manager.h + create mode 100644 gedit/gedit-open-document-selector-helper.c + create mode 100644 gedit/gedit-open-document-selector-helper.h + create mode 100644 gedit/gedit-open-document-selector-store.c + create mode 100644 gedit/gedit-open-document-selector-store.h + create mode 100644 gedit/gedit-open-document-selector.c + create mode 100644 gedit/gedit-open-document-selector.h + create mode 100644 gedit/gedit-pango.c + create mode 100644 gedit/gedit-pango.h + create mode 100644 gedit/gedit-progress-info-bar.c + create mode 100644 gedit/gedit-progress-info-bar.h + delete mode 100644 gedit/gedit-recent-osx.c + delete mode 100644 gedit/gedit-recent-osx.h + create mode 100644 gedit/gedit-view-centering.c + create mode 100644 gedit/gedit-view-centering.h + create mode 100644 gedit/resources/ui/gedit-highlight-mode-dialog.ui + create mode 100644 gedit/resources/ui/gedit-highlight-mode-selector.ui + create mode 100644 gedit/resources/ui/gedit-open-document-selector.ui + create mode 100644 gedit/resources/ui/gedit-progress-info-bar.ui + +diff --git a/NEWS b/NEWS +index d1f17abd1..fabc374cd 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,30 +1,42 @@ ++News in [unreleased] ++---------------------------- ++* Deteplification: ++ - The tepl maintainer has "frozen" the project until further notice ++ - Moving things to tepl introduced some breakage, such as translation of the ++ default filename ++ - gedit didn't use much of the API anyway ++ - Used even less of amtk ++ - Revert all usage of amtk and tepl ++ - Unfortunatly a couple bug fixes may have been lost ++ - Plugin API is essentially reverted to 3.36 ++ + News in 40, 2021-03-19 + ---------------------------- + * Remove all the deprecated API. + * file-browser: drop use of confirm-trash + * Translation updates. + + Tepl-ification: + * Use tepl_pango_font_description_to_css(). + * Use TeplLanguageChooser's. + * Use TeplProgressInfoBar. + * Start to use TeplBuffer and TeplFile APIs. + As a result the new tabs are now named "Untitled File", not + "Untitled Document". + + News in 3.38.1, 2020-11-20 + -------------------------- + * AppData: change donation URL to Liberapay. + * Improvements to the user manual. + * Translation updates. + + News in 3.38.0, 2020-09-11 + -------------------------- + * Translation updates. + + News in 3.37.92, 2020-09-04 + --------------------------- + * A few code refactorings. + * CI: small update. + * Translation updates. + +diff --git a/build-aux/flatpak/org.gnome.gedit.yml b/build-aux/flatpak/org.gnome.gedit.yml +index 1438abd07..d3b535176 100644 +--- a/build-aux/flatpak/org.gnome.gedit.yml ++++ b/build-aux/flatpak/org.gnome.gedit.yml +@@ -32,56 +32,51 @@ cleanup: + - "/share/aclocal" + - "/man" + - "/share/man" + - "/share/gtk-doc" + - "/share/vala" + - "/share/gir-1.0" + - "*.la" + - "*.a" + + modules: + - name: libpeas + buildsystem: meson + config-opts: + - "-Dlua51=false" + - "-Dvapi=true" + - "-Ddemos=false" + - "-Dglade_catalog=false" + sources: + - type: archive + url: https://download.gnome.org/sources/libpeas/1.28/libpeas-1.28.0.tar.xz + sha256: 42d91993b46ed50f16add6d9577ecc22beb8e2dffa7101e2232c2b63733b8b15 + + - name: gspell + cleanup: + - "/bin" + sources: + - type: archive + url: https://download.gnome.org/sources/gspell/1.9/gspell-1.9.1.tar.xz + sha256: dcbb769dfdde8e3c0a8ed3102ce7e661abbf7ddf85df08b29915e92cd723abdd + +- - name: amtk +- buildsystem: meson ++ - name: uchardet ++ buildsystem: cmake-ninja + sources: +- - type: git +- url: https://gitlab.gnome.org/GNOME/amtk.git +- +- - name: tepl +- buildsystem: meson +- sources: +- - type: git +- url: https://gitlab.gnome.org/GNOME/tepl.git ++ - type: archive ++ url: https://www.freedesktop.org/software/uchardet/releases/uchardet-0.0.6.tar.xz ++ sha256: 8351328cdfbcb2432e63938721dd781eb8c11ebc56e3a89d0f84576b96002c61 + + - name: gedit + buildsystem: meson + sources: + - type: git + url: https://gitlab.gnome.org/GNOME/gedit.git + # To build a local branch, comment out 'url' and uncomment: + # path: ../../ + # branch: wip/misc + + - name: gedit-plugins + buildsystem: meson + sources: + - type: git + url: https://gitlab.gnome.org/GNOME/gedit-plugins.git +diff --git a/build-aux/snap/snapcraft.yaml b/build-aux/snap/snapcraft.yaml +index fc49c3949..43db5e027 100644 +--- a/build-aux/snap/snapcraft.yaml ++++ b/build-aux/snap/snapcraft.yaml +@@ -14,121 +14,98 @@ layout: + symlink: $SNAP/usr/share/gedit + + slots: + # for GtkApplication registration + gedit: + interface: dbus + bus: session + name: org.gnome.gedit + + apps: + gedit: + command: usr/bin/gedit + extensions: [gnome-3-28] + plugs: + - avahi-observe + - cups-control + - gsettings + - home + - network + - mount-observe + - removable-media + common-id: org.gnome.gedit.desktop + environment: + GSETTINGS_SCHEMA_DIR: $SNAP/share/glib-2.0/schemas + LD_LIBRARY_PATH: $LD_LIBRARY_PATH:$SNAP/lib/$SNAPCRAFT_ARCH_TRIPLET/gedit:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET:$SNAP/gnome-platform/usr/lib/$SNAPCRAFT_ARCH_TRIPLET + GI_TYPELIB_PATH: $SNAP/usr/lib/girepository-1.0:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/gedit/girepository-1.0:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/girepository-1.0:$SNAP/gnome-platform/usr/lib/girepository-1.0:$SNAP/gnome-platform/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/girepository-1.0 + PYTHONPATH: $SNAP/usr/lib/python3/dist-packages:$SNAP/gnome-platform/usr/lib/python3/dist-packages + GTK_USE_PORTAL: 1 + + parts: +- amtk: +- source: https://gitlab.gnome.org/GNOME/amtk.git +- source-type: git +- plugin: autotools +- configflags: +- - --prefix=/usr +- - --enable-introspection=no +- organize: +- snap/gedit/current/usr: usr +- +- tepl: +- after: [amtk, gtksourceview] +- source: https://gitlab.gnome.org/GNOME/tepl.git +- source-type: git +- plugin: meson +- meson-parameters: +- - --prefix=/usr +- organize: +- snap/gedit/current/usr: usr +- build-packages: +- - libuchardet-dev +- stage-packages: +- - libuchardet0 +- + gtksourceview: + source: https://gitlab.gnome.org/GNOME/gtksourceview.git + source-type: git + plugin: meson + meson-parameters: + - --prefix=/usr + - -Dc_args="-Wno-error=undef" + organize: + snap/gedit/current/usr: usr + build-packages: + - autoconf-archive + - gettext + - gobject-introspection + - gtk-doc-tools + - libfribidi-dev + - libgirepository1.0-dev + - libglib2.0-dev + - libgtk-3-dev + - libpeas-dev + - libxml2-dev + - libxml2-utils + - valac + # meson gir overrides don't work well + override-build: | + snapcraftctl build + cp $SNAPCRAFT_PART_INSTALL/usr/share/gir-1.0/GtkSource* /usr/share/gir-1.0 + cp $SNAPCRAFT_PART_INSTALL/usr/share/vala/vapi/gtksource* /usr/share/vala/vapi + + gedit: +- after: [gtksourceview, tepl] ++ after: [gtksourceview] + source: . + source-type: git + parse-info: [usr/share/metainfo/org.gnome.gedit.appdata.xml] + plugin: meson + meson-parameters: + - --prefix=/usr + - -Dvala_args="--vapidir=$SNAPCRAFT_STAGE/usr/share/vala/vapi" ++ + build-environment: + - C_INCLUDE_PATH: $SNAPCRAFT_STAGE/usr/include/gtksourceview-4 + override-build: | + sed -i.bak -e 's|Icon=org.gnome.gedit$|Icon=${SNAP}/meta/gui/org.gnome.gedit.svg|g' data/org.gnome.gedit.desktop.in + sed -i.bak -e "s|symlink_media: true|symlink_media: false|g" help/meson.build + snapcraftctl build + mkdir -p $SNAPCRAFT_PART_INSTALL/meta/gui/ + cp data/icons/org.gnome.gedit.svg $SNAPCRAFT_PART_INSTALL/meta/gui/ + cp ../install/usr/share/applications/org.gnome.gedit.desktop $SNAPCRAFT_PART_INSTALL/meta/gui/ + build-packages: + - desktop-file-utils + - gettext + - gsettings-desktop-schemas-dev + - gtk-doc-tools + - itstool + - libsoup2.4-dev + - libgspell-1-dev + - libxml2-dev + - libglib2.0-dev + - libgtk-3-dev + - libpeas-dev + - libx11-dev + - python3 + - python-gi-dev + - gobject-introspection + - libgirepository1.0-dev + - valac + stage-packages: + - libfribidi0 + +diff --git a/data/org.gnome.gedit.gschema.xml.in b/data/org.gnome.gedit.gschema.xml.in +index b797d843c..59325f4ef 100644 +--- a/data/org.gnome.gedit.gschema.xml.in ++++ b/data/org.gnome.gedit.gschema.xml.in +@@ -99,60 +99,65 @@ + + true + Automatic indent + Whether gedit should enable automatic indentation. + + + true + Display Line Numbers + Whether gedit should display line numbers in the editing area. + + + true + Highlight Current Line + Whether gedit should highlight the current line. + + + true + Highlight Matching Brackets + Whether gedit should highlight matching brackets. + + + false + Display Right Margin + Whether gedit should display the right margin in the editing area. + + + 80 + Right Margin Position + Specifies the position of the right margin. + ++ ++ false ++ Display Overview Map ++ Whether gedit should display the overview map for the document. ++ + + 'none' + Document background pattern type + Whether the document will get a background pattern painted. + + + + + + + + + 'after' + Smart Home End + Specifies how the cursor moves when the HOME and END keys are pressed. Use “disabled” to always move at the start/end of the line, “after” to move to the start/end of the line the first time the keys are pressed and to the start/end of the text ignoring whitespaces the second time the keys are pressed, “before” to move to the start/end of the text before moving to the start/end of the line and “always” to always move to the start/end of the text instead of the start/end of the line. + + + true + Restore Previous Cursor Position + Whether gedit should restore the previous cursor position when a file is loaded. + + + true + Enable Syntax Highlighting + Whether gedit should enable syntax highlighting. + + + true + Enable Search Highlighting + Whether gedit should highlight all the occurrences of the searched text. +diff --git a/docs/gedit-development-getting-started.md b/docs/gedit-development-getting-started.md +index 4f930afc7..774bc180f 100644 +--- a/docs/gedit-development-getting-started.md ++++ b/docs/gedit-development-getting-started.md +@@ -4,65 +4,63 @@ gedit development - getting started + The following explanations can be improved over time, if you see something + missing, a feedback is welcome. + + Programming languages and paradigms + ----------------------------------- + + gedit is mostly written in C, with some plugins in Python or + [Vala](https://wiki.gnome.org/Projects/Vala/). The build system is in + [Meson](https://mesonbuild.com/). + + The code is object-oriented and event-driven. In C, it's thanks to the use of + the GObject library (see next section). If you open some `*.c` or `*.h` files, + you may think “what is this horror?!” but – don't panic – it's just some + C/GObject boilerplate code, and that boilerplate can be generated by a tool. So + once you've learned GObject, you will no longer be afraid ;-) + + Libraries used + -------------- + + As every GNOME application, gedit uses the GLib, GObject and GTK libraries. To + modify the gedit source code, you should be familiar with those libraries. See + the [GTK website](https://www.gtk.org/) and the document + [The GLib/GTK Development Platform – A Getting Started Guide](https://people.gnome.org/~swilmet/glib-gtk-book/). + + The main widget used by gedit is GtkTextView, a general-purpose multiline text + editor. To learn that widget API, read the excellent + [GtkTextView tutorial](http://www.bravegnu.org/gtktext/) (a bit old but still + mostly valid). But GtkTextView is not enough for source code edition. gedit + actually uses the + [GtkSourceView](https://wiki.gnome.org/Projects/GtkSourceView) library, which +-contains a subclass of GtkTextView with many features useful for a text editor +-or an IDE. But GtkSourceView is not enough to have a full-blown text editor, +-gedit is actually in the process of using more features from the +-[Tepl](https://wiki.gnome.org/Projects/Tepl) library, and to further develop +-Tepl alongside gedit. ++contains a subclass of GtkTextView with syntax highlighting, a completion ++framework, the search and replace, and many other features useful for a text ++editor or an IDE. + + For its plugin system, gedit uses the + [libpeas](https://wiki.gnome.org/Projects/Libpeas) library. + + Plugins may have other dependencies, for example the spell-checking plugin uses + [gspell](https://wiki.gnome.org/Projects/gspell). + + gedit architecture + ------------------ + + The [gedit Git repository](https://gitlab.gnome.org/GNOME/gedit) contains the + _gedit core_ plus the default plugins. There is also the + [gedit-plugins Git repository](https://gitlab.gnome.org/GNOME/gedit-plugins) + for additional official plugins. The gedit core source code is in the `gedit/` + directory. The plugins are in … `plugins/`! + + gedit core provides: + - A basic text editor. + - The integration of libpeas, with an API for plugins. + + There is a class diagram of gedit core in the file + [class-diagram.dia](class-diagram.dia) (but it may be outdated, see the Git log + for that file). + + Build/Installation + ------------------ + + See the file [build.md](build.md). + + First contribution +diff --git a/docs/reference/api-breaks.xml b/docs/reference/api-breaks.xml +index f631761a6..f03c35975 100644 +--- a/docs/reference/api-breaks.xml ++++ b/docs/reference/api-breaks.xml +@@ -1,53 +1,103 @@ + + + ]> + + + API Breaks + + + gedit is a quite old piece of software (created in 1998, at the beginnings + of the GNOME project), and as every software, the code evolves during its + lifetime. So there are sometimes API breaks for gedit plugins, there are no + API stability guarantees. + + + + When it is possible, instead of directly removing an API, that API is first + marked as deprecated, and then removed for the next API break. See the + index of deprecated symbols. + + ++ ++ 40 -> 41 ++ ++ ++ ++ The GeditProgressInfoBar class has been restored ++ ++ ++ ++ ++ GeditDocument is no longer a subclass ++ of TeplBuffer. ++ ++ ++ ++ ++ The GeditDocument::cursor-moved signal has been restored. ++ ++ ++ ++ ++ The GeditDocument:shortname property has been restored. ++ ++ ++ ++ ++ The gedit_document_is_untouched() function has been ++ restored. ++ ++ ++ ++ ++ The gedit_view_set_font() function has been restored. ++ ++ ++ ++ ++ DEBUG_METADATA has been restored. ++ ++ ++ ++ ++ The GBOOLEAN_TO_POINTER() and ++ GPOINTER_TO_BOOLEAN() macros have been restored to ++ gedit-utils.h. ++ ++ ++ ++ ++ + + 3.38 -> 40 + + + + All previously deprecated APIs have been removed. + + + + + The GeditProgressInfoBar class has been removed, you can + use TeplProgressInfoBar + instead. + + + + + GeditDocument is now a subclass + of TeplBuffer. + + + + + The GeditDocument::cursor-moved signal has been removed. + You can use the TeplBuffer API + instead. + + + + +diff --git a/docs/reference/gedit-docs.xml b/docs/reference/gedit-docs.xml +index a0dc624cf..9e32e5469 100644 +--- a/docs/reference/gedit-docs.xml ++++ b/docs/reference/gedit-docs.xml +@@ -1,48 +1,49 @@ + + + + + gedit Reference Manual + + + + API Reference + + + + + + + + ++ + + + + + + + + + + + + + + Annexes + + + Object Hierarchy + + + + + + + Index of all symbols + + + + Index of deprecated symbols + + +diff --git a/docs/reference/gedit-sections.txt b/docs/reference/gedit-sections.txt +index cec055fce..a71e00faa 100644 +--- a/docs/reference/gedit-sections.txt ++++ b/docs/reference/gedit-sections.txt +@@ -27,61 +27,64 @@ gedit_app_activatable_activate + gedit_app_activatable_deactivate + gedit_app_activatable_extend_menu + + GEDIT_TYPE_APP_ACTIVATABLE + GEDIT_APP_ACTIVATABLE + GEDIT_APP_ACTIVATABLE_IFACE + GEDIT_IS_APP_ACTIVATABLE + GEDIT_APP_ACTIVATABLE_GET_IFACE + gedit_app_activatable_get_type + + +
+ gedit-commands + GeditCommands + gedit_commands_load_location + gedit_commands_load_locations + gedit_commands_save_document + gedit_commands_save_document_async + gedit_commands_save_document_finish + gedit_commands_save_all_documents +
+ +
+ gedit-document + GeditDocument + GeditDocument + gedit_document_new + gedit_document_get_file + gedit_document_get_short_name_for_display + gedit_document_get_mime_type ++gedit_document_is_untouched + gedit_document_is_untitled ++gedit_document_goto_line ++gedit_document_goto_line_offset + gedit_document_set_language + gedit_document_get_content_type + gedit_document_get_metadata + gedit_document_set_metadata + gedit_document_set_search_context + gedit_document_get_search_context + + GEDIT_DOCUMENT + GEDIT_IS_DOCUMENT + GEDIT_TYPE_DOCUMENT + gedit_document_get_type + GEDIT_DOCUMENT_CLASS + GEDIT_IS_DOCUMENT_CLASS + GEDIT_DOCUMENT_GET_CLASS +
+ +
+ gedit-encodings-combo-box + GeditEncodingsComboBox + GeditEncodingsComboBox + gedit_encodings_combo_box_new + gedit_encodings_combo_box_get_selected_encoding + gedit_encodings_combo_box_set_selected_encoding + + GEDIT_ENCODINGS_COMBO_BOX + GEDIT_IS_ENCODINGS_COMBO_BOX + GEDIT_TYPE_ENCODINGS_COMBO_BOX + gedit_encodings_combo_box_get_type + GEDIT_ENCODINGS_COMBO_BOX_CLASS + GEDIT_IS_ENCODINGS_COMBO_BOX_CLASS +@@ -121,111 +124,138 @@ GEDIT_TYPE_MESSAGE_BUS + gedit_message_bus_get_type + GEDIT_MESSAGE_BUS_CLASS + GEDIT_IS_MESSAGE_BUS_CLASS + GEDIT_MESSAGE_BUS_GET_CLASS + GeditMessageBusPrivate +
+ +
+ gedit-message + GeditMessage + GeditMessage + gedit_message_get_object_path + gedit_message_get_method + gedit_message_type_has + gedit_message_type_check + gedit_message_has + gedit_message_is_valid_object_path + gedit_message_type_identifier + + GEDIT_MESSAGE + GEDIT_MESSAGE_CONST + GEDIT_IS_MESSAGE + GEDIT_TYPE_MESSAGE + gedit_message_get_type + GEDIT_MESSAGE_CLASS + GEDIT_IS_MESSAGE_CLASS + GEDIT_MESSAGE_GET_CLASS + GeditMessagePrivate +
+ ++
++gedit-progress-info-bar ++GeditProgressInfoBar ++GeditProgressInfoBar ++gedit_progress_info_bar_new ++gedit_progress_info_bar_set_icon_name ++gedit_progress_info_bar_set_markup ++gedit_progress_info_bar_set_text ++gedit_progress_info_bar_set_fraction ++gedit_progress_info_bar_pulse ++ ++GEDIT_PROGRESS_INFO_BAR ++GEDIT_IS_PROGRESS_INFO_BAR ++GEDIT_TYPE_PROGRESS_INFO_BAR ++gedit_progress_info_bar_get_type ++GEDIT_PROGRESS_INFO_BAR_CLASS ++GEDIT_IS_PROGRESS_INFO_BAR_CLASS ++GEDIT_PROGRESS_INFO_BAR_GET_CLASS ++
++ +
+ gedit-statusbar + GeditStatusbar + GeditStatusbar + gedit_statusbar_new + gedit_statusbar_set_window_state + gedit_statusbar_set_overwrite + gedit_statusbar_clear_overwrite + gedit_statusbar_flash_message + + GEDIT_STATUSBAR + GEDIT_IS_STATUSBAR + GEDIT_TYPE_STATUSBAR + gedit_statusbar_get_type + GEDIT_STATUSBAR_CLASS + GEDIT_IS_STATUSBAR_CLASS + GEDIT_STATUSBAR_GET_CLASS +
+ +
+ gedit-tab + GeditTab + GeditTab + GeditTabState + gedit_tab_get_view + gedit_tab_get_document + gedit_tab_get_from_document + gedit_tab_get_state + gedit_tab_get_auto_save_enabled + gedit_tab_set_auto_save_enabled + gedit_tab_get_auto_save_interval + gedit_tab_set_auto_save_interval + gedit_tab_set_info_bar + + GEDIT_TAB + GEDIT_IS_TAB + GEDIT_TYPE_TAB + gedit_tab_get_type + GEDIT_TAB_CLASS + GEDIT_IS_TAB_CLASS + GEDIT_TAB_GET_CLASS + GEDIT_TYPE_TAB_STATE + gedit_tab_state_get_type +
+ +
+ gedit-view + GeditViewPrivate + GeditView + GeditView + gedit_view_new ++gedit_view_cut_clipboard ++gedit_view_copy_clipboard ++gedit_view_paste_clipboard ++gedit_view_delete_selection ++gedit_view_select_all ++gedit_view_scroll_to_cursor ++gedit_view_set_font + + GEDIT_VIEW + GEDIT_IS_VIEW + GEDIT_TYPE_VIEW + gedit_view_get_type + GEDIT_VIEW_CLASS + GEDIT_IS_VIEW_CLASS + GEDIT_VIEW_GET_CLASS +
+ +
+ gedit-view-activatable + GeditViewActivatable + GeditViewActivatable + gedit_view_activatable_activate + gedit_view_activatable_deactivate + + GEDIT_TYPE_VIEW_ACTIVATABLE + GEDIT_VIEW_ACTIVATABLE + GEDIT_VIEW_ACTIVATABLE_IFACE + GEDIT_IS_VIEW_ACTIVATABLE + GEDIT_VIEW_ACTIVATABLE_GET_IFACE + gedit_view_activatable_get_type +
+ +
+ gedit-window + GeditWindow + GeditWindow + GeditWindowState +@@ -264,70 +294,76 @@ gedit_window_state_get_type + +
+ gedit-window-activatable + GeditWindowActivatable + GeditWindowActivatable + gedit_window_activatable_activate + gedit_window_activatable_deactivate + gedit_window_activatable_update_state + + GEDIT_TYPE_WINDOW_ACTIVATABLE + GEDIT_WINDOW_ACTIVATABLE + GEDIT_WINDOW_ACTIVATABLE_IFACE + GEDIT_IS_WINDOW_ACTIVATABLE + GEDIT_WINDOW_ACTIVATABLE_GET_IFACE + gedit_window_activatable_get_type +
+ +
+ gedit-debug + GeditDebugSection + DEBUG_VIEW + DEBUG_PREFS + DEBUG_WINDOW + DEBUG_PANEL + DEBUG_PLUGINS + DEBUG_TAB + DEBUG_DOCUMENT + DEBUG_COMMANDS + DEBUG_APP + DEBUG_UTILS ++DEBUG_METADATA + gedit_debug_init + gedit_debug + gedit_debug_message + gedit_debug_plugin_message + + GEDIT_TYPE_DEBUG_SECTION + gedit_debug_section_get_type +
+ +
+ gedit-menu-extension + GeditMenuExtension + gedit_menu_extension_new + gedit_menu_extension_append_menu_item + gedit_menu_extension_prepend_menu_item + gedit_menu_extension_remove_items + + GEDIT_IS_MENU_EXTENSION + GEDIT_IS_MENU_EXTENSION_CLASS + GEDIT_MENU_EXTENSION + GEDIT_MENU_EXTENSION_CLASS + GEDIT_MENU_EXTENSION_CONST + GEDIT_MENU_EXTENSION_GET_CLASS + GEDIT_TYPE_MENU_EXTENSION + GeditMenuExtensionClass + gedit_menu_extension_get_type +
+ +
+ gedit-utils ++GBOOLEAN_TO_POINTER ++GPOINTER_TO_BOOLEAN + gedit_utils_menu_position_under_tree_view + gedit_utils_set_atk_name_description ++gedit_warning ++gedit_utils_replace_home_dir_with_tilde + gedit_utils_basename_for_display ++gedit_utils_decode_uri + gedit_utils_drop_get_uris + gedit_utils_get_compression_type_from_content_type + gedit_utils_is_valid_location + gedit_utils_location_get_dirname_for_display + gedit_utils_set_direct_save_filename + gedit_utils_newline_type_to_string +
+diff --git a/docs/reference/meson.build b/docs/reference/meson.build +index 9a9c414dc..ed85f9cec 100644 +--- a/docs/reference/meson.build ++++ b/docs/reference/meson.build +@@ -1,42 +1,38 @@ + html_dir = get_option('prefix') / gnome.gtkdoc_html_dir('gedit') + + glib_docpath = dependency('glib-2.0').get_pkgconfig_variable('prefix') / 'share/gtk-doc/html/glib' + gobject_docpath = dependency('gobject-2.0').get_pkgconfig_variable('prefix') / 'share/gtk-doc/html/gobject' + gio_docpath = dependency('gio-2.0').get_pkgconfig_variable('prefix') / 'share/gtk-doc/html/gio' + gdk_docpath = dependency('gdk-3.0').get_pkgconfig_variable('prefix') / 'share/gtk-doc/html/gdk3' + gtk_docpath = dependency('gtk+-3.0').get_pkgconfig_variable('prefix') / 'share/gtk-doc/html/gtk3' + gsv_docpath = dependency('gtksourceview-4').get_pkgconfig_variable('prefix') / 'share/gtk-doc/html/gtksourceview-4.0' +-amtk_docpath = dependency('amtk-5').get_pkgconfig_variable('prefix') / 'share/gtk-doc/html/amtk-5' +-tepl_docpath = dependency('tepl-6').get_pkgconfig_variable('prefix') / 'share/gtk-doc/html/tepl-6' + libpeas_docpath = dependency('libpeas-1.0').get_pkgconfig_variable('prefix') / 'share/gtk-doc/html/libpeas' + + gedit_doc_dep = declare_dependency( + link_with: libgedit_shared_lib, + include_directories: root_include_dir, +- dependencies: deps_basic_list, ++ dependencies: deps_basic_list + [libxml_dep], + ) + + gnome.gtkdoc( + 'gedit', + main_xml: 'gedit-docs.xml', + src_dir: include_directories('../../gedit/'), + dependencies: gedit_doc_dep, + scan_args: ['--rebuild-types'], + fixxref_args: [ + '--html-dir=@0@'.format(html_dir), + '--extra-dir=@0@'.format(glib_docpath), + '--extra-dir=@0@'.format(gobject_docpath), + '--extra-dir=@0@'.format(gio_docpath), + '--extra-dir=@0@'.format(gdk_docpath), + '--extra-dir=@0@'.format(gtk_docpath), + '--extra-dir=@0@'.format(gsv_docpath), +- '--extra-dir=@0@'.format(amtk_docpath), +- '--extra-dir=@0@'.format(tepl_docpath), + '--extra-dir=@0@'.format(libpeas_docpath), + ], + content_files: [ + 'api-breaks.xml' + ], + ignore_headers: libgedit_private_headers, + install: true, + ) +diff --git a/docs/roadmap-done.md b/docs/roadmap-done.md +deleted file mode 100644 +index 7f210e49b..000000000 +--- a/docs/roadmap-done.md ++++ /dev/null +@@ -1,56 +0,0 @@ +-gedit roadmap - done tasks +-========================== +- +-Tepl-ification of the gedit core +--------------------------------- +- +-- gedit 3.36: +- - Start to use the Tepl library. +- - Use some Tepl utility functions. +- - Use TeplFileMetadata, remove GeditMetadataManager. +-- gedit 3.38: +- - Move some utility functions to the Tepl library. +- - Refactor and move some I/O error infobars to Tepl. +- - GeditView now inherits from TeplView. +- - Port to the new Tepl metadata API. +- - Use TeplStyleSchemeChooserWidget in the preferences dialog. +- - Create GeditFactory class, subclass of TeplAbstractFactory. +-- gedit 40: +- - Use `tepl_pango_font_description_to_css()`. +- - Use TeplLanguageChooser's, for choosing a language for the syntax +- highlighting. Remove GeditHighlightModeSelector and +- GeditHighlightModeDialog. +- - Use TeplProgressInfoBar. Remove GeditProgressInfoBar. +- - GeditDocument now inherits from TeplBuffer, start to use the +- TeplBuffer and TeplFile APIs. +- +-Links: +-- https://wiki.gnome.org/Projects/Tepl +- +-Tepl-ification of the gedit plugins +------------------------------------ +- +-- gedit 40: +- - Draw Spaces plugin: new implementation based on TeplSpaceDrawerPrefs. +- +-Other done tasks in gedit plugins +---------------------------------- +- +-- gedit 40: +- - Smart Spaces plugin: new implementation based on a GtkSourceView +- feature. +- +-New version of gedit on Windows +-------------------------------- +- +-[gedit is now available on the Microsoft Store](https://www.microsoft.com/store/apps/9PL1J21XF0PT). +-It was done during the GNOME 3.38 development cycle. The integration with +-Windows is not perfect, but it works. It is planned to improve gedit for +-Windows over time. +- +-Documentation for contributors +------------------------------- +- +-Write a guide to get started with gedit development. +- +-Done during the GNOME 3.34 development cycle. +diff --git a/docs/roadmap.md b/docs/roadmap.md +index 44a2f00b7..ec9445712 100644 +--- a/docs/roadmap.md ++++ b/docs/roadmap.md +@@ -1,57 +1,40 @@ + gedit roadmap + ============= + + This page contains the plans for major code changes we hope to get done in the + future. + +-See the [roadmap-done.md](roadmap-done.md) file for done tasks. ++See also the [GtkSourceView](https://wiki.gnome.org/Projects/GtkSourceView/RoadMap). + + See the [NEWS file](../NEWS) for a detailed history. + +-See also the +-[Tepl roadmap](https://gitlab.gnome.org/GNOME/tepl/blob/master/docs/roadmap.md). +- +-Continue to make the gedit source code more re-usable +------------------------------------------------------ +- +-Status: **in progress** (this is an ongoing effort) +- +-Next steps: +-- Use more features from the Tepl library, and develop Tepl alongside gedit. +- The goal is to reduce the amount of code in gedit, by having re-usable code +- in Tepl instead. +- +-Links: +-- https://wiki.gnome.org/Apps/Gedit/ReusableCode +-- https://wiki.gnome.org/Projects/Tepl +- + Improve gedit on Windows + ------------------------ + + Status: **in progress** + + [gedit is now available on the Microsoft Store](https://www.microsoft.com/store/apps/9PL1J21XF0PT). + The integration with Windows is not perfect, but it works. It is planned to + improve gedit for Windows over time. + + Replace search and replace dialog window by an horizontal bar below the text + ---------------------------------------------------------------------------- + + Status: **todo** + + To not hide the text. + + Be able to quit the application with all documents saved, and restored on next start + ------------------------------------------------------------------------------------ + + Status: **todo** + + Even for unsaved and untitled files, be able to quit gedit, restart it later and + come back to the state before with all tabs restored. + + Improve the workflow for printing to paper + ------------------------------------------ + + Status: **todo** + + Implement it like in Firefox, show first a preview of the file to print. +diff --git a/gedit/Gedit-3.0.metadata b/gedit/Gedit-3.0.metadata +index e36d7cb30..1a5b45a3a 100644 +--- a/gedit/Gedit-3.0.metadata ++++ b/gedit/Gedit-3.0.metadata +@@ -1,23 +1,24 @@ + App cheader_filename="gedit/gedit-app.h" + AppActivatable cheader_filename="gedit/gedit-app-activatable.h" + DebugSection cheader_filename="gedit/gedit-debug.h" + Document cheader_filename="gedit/gedit-document.h" + EncodingsComboBox cheader_filename="gedit/gedit-encodings-combo-box.h" + MenuExtension cheader_filename="gedit/gedit-menu-extension.h" + Message cheader_filename="gedit/gedit-message.h" + MessageBus cheader_filename="gedit/gedit-message-bus.h" ++ProgressInfoBar cheader_filename="gedit/gedit-progress-info-bar.h" + Statusbar cheader_filename="gedit/gedit-statusbar.h" + Tab cheader_filename="gedit/gedit-tab.h" + TabState cheader_filename="gedit/gedit-tab.h" + View cheader_filename="gedit/gedit-view.h" + ViewActivatable cheader_filename="gedit/gedit-view-activatable.h" + Window cheader_filename="gedit/gedit-window.h" + WindowActivatable cheader_filename="gedit/gedit-window-activatable.h" + WindowState cheader_filename="gedit/gedit-window.h" + + commands_* cheader_filename="gedit/gedit-commands.h" + debug* cheader_filename="gedit/gedit-debug.h" + utils_* cheader_filename="gedit/gedit-utils.h" + + MessageBusForeach cheader_filename="gedit/gedit-message-bus.h" + MessageCallback cheader_filename="gedit/gedit-message-bus.h" +diff --git a/gedit/gedit-app-osx.m b/gedit/gedit-app-osx.m +index b02e9a28b..5df1b94da 100644 +--- a/gedit/gedit-app-osx.m ++++ b/gedit/gedit-app-osx.m +@@ -4,61 +4,61 @@ + * + * Copyright (C) 2010 - Jesse van den Kieboom + * + * gedit 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. + * + * gedit 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 gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + + #include "gedit-app-osx.h" + + #include + #include + #include + + #include "gedit-app-private.h" + #include "gedit-dirs.h" + #include "gedit-debug.h" + #include "gedit-commands.h" + #include "gedit-commands-private.h" +-#include "gedit-recent-osx.h" ++#include "gedit-recent.h" + #import + + NSWindow *gdk_quartz_window_get_nswindow(GdkWindow *window); + NSEvent *gdk_quartz_event_get_nsevent(GdkEvent *event); + + static GeditWindow * + ensure_window (GeditAppOSX *app, + gboolean with_empty_document) + { + GList *windows; + GeditWindow *ret = NULL; + + windows = gtk_application_get_windows (GTK_APPLICATION (app)); + + while (windows) + { + GtkWindow *window; + GdkWindow *win; + NSWindow *nswin; + + window = windows->data; + windows = g_list_next (windows); + + if (!gtk_widget_get_realized (GTK_WIDGET (window))) + { + continue; + } + + if (!GEDIT_IS_WINDOW (window)) + { +@@ -259,61 +259,61 @@ gedit_app_osx_set_window_title_impl (GeditApp *app, + } + + native = gdk_quartz_window_get_nswindow (wnd); + document = gedit_window_get_active_document (window); + + if (document) + { + bool ismodified; + + if (gedit_document_is_untitled (document)) + { + [native setRepresentedURL:nil]; + } + else + { + GtkSourceFile *file; + GFile *location; + gchar *uri; + + file = gedit_document_get_file (document); + location = gtk_source_file_get_location (file); + + uri = g_file_get_uri (location); + + NSURL *nsurl = [NSURL URLWithString:[NSString stringWithUTF8String:uri]]; + + [native setRepresentedURL:nsurl]; + g_free (uri); + } + +- ismodified = !tepl_buffer_is_untouched (TEPL_BUFFER (document)); ++ ismodified = !gedit_document_is_untouched (document); + [native setDocumentEdited:ismodified]; + } + else + { + [native setRepresentedURL:nil]; + [native setDocumentEdited:false]; + } + + GEDIT_APP_CLASS (gedit_app_osx_parent_class)->set_window_title (app, window, title); + } + + typedef struct + { + GeditAppOSX *app; + GtkRecentInfo *info; + } RecentFileInfo; + + static void + recent_file_info_free (gpointer data, + GClosure *closure) + { + RecentFileInfo *info = data; + + g_object_unref (info->app); + gtk_recent_info_unref (info->info); + + g_slice_free (RecentFileInfo, data); + } + + static void +diff --git a/gedit/gedit-app-private.h b/gedit/gedit-app-private.h +index 6e1278a3b..e9c58cc27 100644 +--- a/gedit/gedit-app-private.h ++++ b/gedit/gedit-app-private.h +@@ -1,52 +1,55 @@ + /* + * gedit-app-private.h + * This file is part of gedit + * + * Copyright (C) 2015 - Sébastien Wilmet + * + * 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, see . + */ + + #ifndef GEDIT_APP_PRIVATE_H + #define GEDIT_APP_PRIVATE_H + + #include "gedit-app.h" ++#include "gedit-metadata-manager.h" + #include "gedit-menu-extension.h" + + G_BEGIN_DECLS + + /* global print config */ + GtkPageSetup *_gedit_app_get_default_page_setup (GeditApp *app); + void _gedit_app_set_default_page_setup (GeditApp *app, + GtkPageSetup *page_setup); + GtkPrintSettings *_gedit_app_get_default_print_settings (GeditApp *app); + void _gedit_app_set_default_print_settings (GeditApp *app, + GtkPrintSettings *settings); + ++GeditMetadataManager *_gedit_app_get_metadata_manager (GeditApp *app); ++ + GMenuModel *_gedit_app_get_hamburger_menu (GeditApp *app); + + GMenuModel *_gedit_app_get_notebook_menu (GeditApp *app); + + GMenuModel *_gedit_app_get_tab_width_menu (GeditApp *app); + + GMenuModel *_gedit_app_get_line_col_menu (GeditApp *app); + + GeditMenuExtension *_gedit_app_extend_menu (GeditApp *app, + const gchar *extension_point); + + G_END_DECLS + + #endif /* GEDIT_APP_PRIVATE_H */ + + /* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-app.c b/gedit/gedit-app.c +index 5532d5975..27d71a87a 100644 +--- a/gedit/gedit-app.c ++++ b/gedit/gedit-app.c +@@ -1,83 +1,92 @@ + /* + * gedit-app.c + * This file is part of gedit + * + * Copyright (C) 2005-2006 - Paolo Maggi + * + * 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, see . + */ + + #include "config.h" + + #include "gedit-app.h" + #include "gedit-app-private.h" + + #include + #include + #include + + #include ++#include + #include +-#include ++#include + + #include "gedit-commands-private.h" + #include "gedit-notebook.h" + #include "gedit-debug.h" + #include "gedit-utils.h" + #include "gedit-enum-types.h" + #include "gedit-dirs.h" + #include "gedit-settings.h" + #include "gedit-app-activatable.h" + #include "gedit-plugins-engine.h" + #include "gedit-commands.h" + #include "gedit-preferences-dialog.h" + #include "gedit-tab.h" + ++#ifndef ENABLE_GVFS_METADATA ++#include "gedit-metadata-manager.h" ++#endif ++ + #define GEDIT_PAGE_SETUP_FILE "gedit-page-setup" + #define GEDIT_PRINT_SETTINGS_FILE "gedit-print-settings" + + typedef struct + { + GeditPluginsEngine *engine; + ++#ifndef ENABLE_GVFS_METADATA ++ GeditMetadataManager *metadata_manager; ++#endif ++ + GtkCssProvider *theme_provider; + + GtkPageSetup *page_setup; + GtkPrintSettings *print_settings; + + GSettings *ui_settings; + GSettings *window_settings; + + GMenuModel *hamburger_menu; + GMenuModel *notebook_menu; + GMenuModel *tab_width_menu; + GMenuModel *line_col_menu; + + PeasExtensionSet *extensions; + + /* command line parsing */ + gboolean new_window; + gboolean new_document; + const GtkSourceEncoding *encoding; + GInputStream *stdin_stream; + GSList *file_list; + gint line_position; + gint column_position; + GApplicationCommandLine *command_line; + } GeditAppPrivate; + + static const GOptionEntry options[] = + { + /* Version */ + { +@@ -118,60 +127,64 @@ static const GOptionEntry options[] = + "wait", 'w', 0, G_OPTION_ARG_NONE, NULL, + N_("Open files and block process until files are closed"), + NULL + }, + + /* New instance */ + { + "standalone", 's', 0, G_OPTION_ARG_NONE, NULL, + N_("Run gedit in standalone mode"), + NULL + }, + + /* collects file arguments */ + { + G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, NULL, NULL, + N_("[FILE…] [+LINE[:COLUMN]]") + }, + + {NULL} + }; + + G_DEFINE_TYPE_WITH_PRIVATE (GeditApp, gedit_app, GTK_TYPE_APPLICATION) + + static void + gedit_app_dispose (GObject *object) + { + GeditAppPrivate *priv; + + priv = gedit_app_get_instance_private (GEDIT_APP (object)); + ++#ifndef ENABLE_GVFS_METADATA ++ g_clear_object (&priv->metadata_manager); ++#endif ++ + g_clear_object (&priv->ui_settings); + g_clear_object (&priv->window_settings); + + g_clear_object (&priv->page_setup); + g_clear_object (&priv->print_settings); + + /* Note that unreffing the extensions will automatically remove + * all extensions which in turn will deactivate the extension + */ + g_clear_object (&priv->extensions); + + g_clear_object (&priv->engine); + + if (priv->theme_provider != NULL) + { + gtk_style_context_remove_provider_for_screen (gdk_screen_get_default (), + GTK_STYLE_PROVIDER (priv->theme_provider)); + g_clear_object (&priv->theme_provider); + } + + g_clear_object (&priv->hamburger_menu); + g_clear_object (&priv->notebook_menu); + g_clear_object (&priv->tab_width_menu); + g_clear_object (&priv->line_col_menu); + + G_OBJECT_CLASS (gedit_app_parent_class)->dispose (object); + } + + static gchar * + gedit_app_help_link_id_impl (GeditApp *app, +@@ -616,71 +629,82 @@ add_accelerator (GtkApplication *app, + const gchar *action_name, + const gchar *accel) + { + const gchar *vaccels[] = { + accel, + NULL + }; + + gtk_application_set_accels_for_action (app, action_name, vaccels); + } + + static gboolean + show_menubar (void) + { + GtkSettings *settings = gtk_settings_get_default (); + gboolean result; + + g_object_get (settings, + "gtk-shell-shows-menubar", &result, + NULL); + + return result; + } + + static void + gedit_app_startup (GApplication *application) + { + GeditAppPrivate *priv; + GtkCssProvider *css_provider; + GtkSourceStyleSchemeManager *manager; ++#ifndef ENABLE_GVFS_METADATA ++ const gchar *cache_dir; ++ gchar *metadata_filename; ++#endif + + priv = gedit_app_get_instance_private (GEDIT_APP (application)); + + G_APPLICATION_CLASS (gedit_app_parent_class)->startup (application); + + /* Setup debugging */ + gedit_debug_init (); + gedit_debug_message (DEBUG_APP, "Startup"); + + setup_theme_extensions (GEDIT_APP (application)); + ++#ifndef ENABLE_GVFS_METADATA ++ cache_dir = gedit_dirs_get_user_cache_dir (); ++ metadata_filename = g_build_filename (cache_dir, "gedit-metadata.xml", NULL); ++ priv->metadata_manager = gedit_metadata_manager_new (metadata_filename); ++ g_free (metadata_filename); ++#endif ++ + /* Load/init settings */ + _gedit_settings_get_singleton (); + priv->ui_settings = g_settings_new ("org.gnome.gedit.preferences.ui"); + priv->window_settings = g_settings_new ("org.gnome.gedit.state.window"); + + g_action_map_add_action_entries (G_ACTION_MAP (application), + app_entries, + G_N_ELEMENTS (app_entries), + application); + + /* menus */ + if (!show_menubar ()) + { + gtk_application_set_menubar (GTK_APPLICATION (application), NULL); + priv->hamburger_menu = get_menu_model (GEDIT_APP (application), + "hamburger-menu"); + } + + priv->notebook_menu = get_menu_model (GEDIT_APP (application), "notebook-menu"); + priv->tab_width_menu = get_menu_model (GEDIT_APP (application), "tab-width-menu"); + priv->line_col_menu = get_menu_model (GEDIT_APP (application), "line-col-menu"); + + /* Accelerators */ + add_accelerator (GTK_APPLICATION (application), "app.new-window", "N"); + add_accelerator (GTK_APPLICATION (application), "app.quit", "Q"); + add_accelerator (GTK_APPLICATION (application), "app.help", "F1"); + add_accelerator (GTK_APPLICATION (application), "app.shortcuts", "question"); + + add_accelerator (GTK_APPLICATION (application), "win.hamburger-menu", "F10"); + add_accelerator (GTK_APPLICATION (application), "win.open", "O"); +@@ -1087,60 +1111,64 @@ save_print_settings (GeditApp *app) + gchar *filename; + GError *error = NULL; + + filename = get_print_settings_file (); + + gtk_print_settings_to_file (priv->print_settings, + filename, + &error); + if (error) + { + g_warning ("%s", error->message); + g_error_free (error); + } + + g_free (filename); + } + } + + static void + gedit_app_shutdown (GApplication *app) + { + gedit_debug_message (DEBUG_APP, "Quitting\n"); + + /* Last window is gone... save some settings and exit */ + ensure_user_config_dir (); + + save_accels (); + save_page_setup (GEDIT_APP (app)); + save_print_settings (GEDIT_APP (app)); + ++ /* GTK+ can still hold references to some gedit objects, for example ++ * GeditDocument for the clipboard. So the metadata-manager should be ++ * shutdown after. ++ */ + G_APPLICATION_CLASS (gedit_app_parent_class)->shutdown (app); + } + + static gboolean + window_delete_event (GeditWindow *window, + GdkEvent *event, + GeditApp *app) + { + GeditWindowState ws; + + ws = gedit_window_get_state (window); + + if (ws & + (GEDIT_WINDOW_STATE_SAVING | GEDIT_WINDOW_STATE_PRINTING)) + { + return TRUE; + } + + _gedit_cmd_file_quit (NULL, NULL, window); + + /* Do not destroy the window */ + return TRUE; + } + + static GeditWindow * + gedit_app_create_window_impl (GeditApp *app) + { + GeditWindow *window; + + window = g_object_new (GEDIT_TYPE_WINDOW, "application", app, NULL); +@@ -1226,69 +1254,64 @@ load_print_settings (GeditApp *app) + + priv->print_settings = gtk_print_settings_new_from_file (filename, &error); + if (error != NULL) + { + /* - Ignore file not found error. + * - Ignore empty file error, i.e. group not found. This happens + * when we click on cancel in the print dialog, when using the + * printing for the first time in gedit. + */ + if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT) && + !g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) + { + g_warning ("Load print settings error: %s", error->message); + } + + g_error_free (error); + } + + g_free (filename); + + /* fall back to default settings */ + if (priv->print_settings == NULL) + { + priv->print_settings = gtk_print_settings_new (); + } + } + + static void + gedit_app_init (GeditApp *app) + { +- TeplApplication *tepl_app; +- + g_set_application_name ("gedit"); + gtk_window_set_default_icon_name ("org.gnome.gedit"); + + g_application_add_main_option_entries (G_APPLICATION (app), options); +- +- tepl_app = tepl_application_get_from_gtk_application (GTK_APPLICATION (app)); +- tepl_application_handle_metadata (tepl_app); + } + + /** + * gedit_app_create_window: + * @app: the #GeditApp + * @screen: (allow-none): + * + * Create a new #GeditWindow part of @app. + * + * Return value: (transfer none): the new #GeditWindow + */ + GeditWindow * + gedit_app_create_window (GeditApp *app, + GdkScreen *screen) + { + GeditAppPrivate *priv; + GeditWindow *window; + GdkWindowState state; + gint w, h; + + gedit_debug (DEBUG_APP); + + priv = gedit_app_get_instance_private (app); + + window = GEDIT_APP_GET_CLASS (app)->create_window (app); + + if (screen != NULL) + { + gtk_window_set_screen (GTK_WINDOW (window), screen); + } +@@ -1544,60 +1567,79 @@ _gedit_app_get_default_print_settings (GeditApp *app) + + priv = gedit_app_get_instance_private (app); + + if (priv->print_settings == NULL) + { + load_print_settings (app); + } + + return gtk_print_settings_copy (priv->print_settings); + } + + void + _gedit_app_set_default_print_settings (GeditApp *app, + GtkPrintSettings *settings) + { + GeditAppPrivate *priv; + + g_return_if_fail (GEDIT_IS_APP (app)); + g_return_if_fail (GTK_IS_PRINT_SETTINGS (settings)); + + priv = gedit_app_get_instance_private (app); + + if (priv->print_settings != NULL) + { + g_object_unref (priv->print_settings); + } + + priv->print_settings = g_object_ref (settings); + } + ++ ++GeditMetadataManager * ++_gedit_app_get_metadata_manager (GeditApp *app) ++{ ++#ifndef ENABLE_GVFS_METADATA ++ GeditAppPrivate *priv; ++ ++ g_return_val_if_fail (GEDIT_IS_APP (app), NULL); ++ ++ priv = gedit_app_get_instance_private (app); ++ ++ return priv->metadata_manager; ++#else ++ g_assert_not_reached (); ++ return NULL; ++#endif ++} ++ ++ + GMenuModel * + _gedit_app_get_hamburger_menu (GeditApp *app) + { + GeditAppPrivate *priv; + + g_return_val_if_fail (GEDIT_IS_APP (app), NULL); + + priv = gedit_app_get_instance_private (app); + + return priv->hamburger_menu; + } + + GMenuModel * + _gedit_app_get_notebook_menu (GeditApp *app) + { + GeditAppPrivate *priv; + + g_return_val_if_fail (GEDIT_IS_APP (app), NULL); + + priv = gedit_app_get_instance_private (app); + + return priv->notebook_menu; + } + + GMenuModel * + _gedit_app_get_tab_width_menu (GeditApp *app) + { + GeditAppPrivate *priv; + + g_return_val_if_fail (GEDIT_IS_APP (app), NULL); +diff --git a/gedit/gedit-commands-edit.c b/gedit/gedit-commands-edit.c +index 66ea0174b..978440150 100644 +--- a/gedit/gedit-commands-edit.c ++++ b/gedit/gedit-commands-edit.c +@@ -17,180 +17,180 @@ + * 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, see . + */ + + #include "config.h" + + #include "gedit-commands.h" + #include "gedit-commands-private.h" + + #include + + #include "gedit-window.h" + #include "gedit-debug.h" + #include "gedit-view.h" + #include "gedit-preferences-dialog.h" + + void + _gedit_cmd_edit_undo (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) + { + GeditWindow *window = GEDIT_WINDOW (user_data); + GeditView *active_view; + GtkSourceBuffer *active_document; + + gedit_debug (DEBUG_COMMANDS); + + active_view = gedit_window_get_active_view (window); +- g_return_if_fail (active_view != NULL); ++ g_return_if_fail (active_view); + + active_document = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (active_view))); + + gtk_source_buffer_undo (active_document); + +- tepl_view_scroll_to_cursor (TEPL_VIEW (active_view)); ++ gedit_view_scroll_to_cursor (active_view); + + gtk_widget_grab_focus (GTK_WIDGET (active_view)); + } + + void + _gedit_cmd_edit_redo (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) + { + GeditWindow *window = GEDIT_WINDOW (user_data); + GeditView *active_view; + GtkSourceBuffer *active_document; + + gedit_debug (DEBUG_COMMANDS); + + active_view = gedit_window_get_active_view (window); +- g_return_if_fail (active_view != NULL); ++ g_return_if_fail (active_view); + + active_document = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (active_view))); + + gtk_source_buffer_redo (active_document); + +- tepl_view_scroll_to_cursor (TEPL_VIEW (active_view)); ++ gedit_view_scroll_to_cursor (active_view); + + gtk_widget_grab_focus (GTK_WIDGET (active_view)); + } + + void + _gedit_cmd_edit_cut (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) + { + GeditWindow *window = GEDIT_WINDOW (user_data); + GeditView *active_view; + + gedit_debug (DEBUG_COMMANDS); + + active_view = gedit_window_get_active_view (window); +- g_return_if_fail (active_view != NULL); ++ g_return_if_fail (active_view); + +- tepl_view_cut_clipboard (TEPL_VIEW (active_view)); ++ gedit_view_cut_clipboard (active_view); + + gtk_widget_grab_focus (GTK_WIDGET (active_view)); + } + + void + _gedit_cmd_edit_copy (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) + { + GeditWindow *window = GEDIT_WINDOW (user_data); + GeditView *active_view; + + gedit_debug (DEBUG_COMMANDS); + + active_view = gedit_window_get_active_view (window); +- g_return_if_fail (active_view != NULL); ++ g_return_if_fail (active_view); + +- tepl_view_copy_clipboard (TEPL_VIEW (active_view)); ++ gedit_view_copy_clipboard (active_view); + + gtk_widget_grab_focus (GTK_WIDGET (active_view)); + } + + void + _gedit_cmd_edit_paste (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) + { + GeditWindow *window = GEDIT_WINDOW (user_data); + GeditView *active_view; + + gedit_debug (DEBUG_COMMANDS); + + active_view = gedit_window_get_active_view (window); +- g_return_if_fail (active_view != NULL); ++ g_return_if_fail (active_view); + +- tepl_view_paste_clipboard (TEPL_VIEW (active_view)); ++ gedit_view_paste_clipboard (active_view); + + gtk_widget_grab_focus (GTK_WIDGET (active_view)); + } + + void + _gedit_cmd_edit_delete (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) + { + GeditWindow *window = GEDIT_WINDOW (user_data); + GeditView *active_view; + + gedit_debug (DEBUG_COMMANDS); + + active_view = gedit_window_get_active_view (window); +- g_return_if_fail (active_view != NULL); ++ g_return_if_fail (active_view); + +- tepl_view_delete_selection (TEPL_VIEW (active_view)); ++ gedit_view_delete_selection (active_view); + + gtk_widget_grab_focus (GTK_WIDGET (active_view)); + } + + void + _gedit_cmd_edit_select_all (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) + { + GeditWindow *window = GEDIT_WINDOW (user_data); + GeditView *active_view; + + gedit_debug (DEBUG_COMMANDS); + + active_view = gedit_window_get_active_view (window); +- g_return_if_fail (active_view != NULL); ++ g_return_if_fail (active_view); + +- tepl_view_select_all (TEPL_VIEW (active_view)); ++ gedit_view_select_all (active_view); + + gtk_widget_grab_focus (GTK_WIDGET (active_view)); + } + + void + _gedit_cmd_edit_preferences (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) + { + GeditWindow *window = GEDIT_WINDOW (user_data); + + gedit_debug (DEBUG_COMMANDS); + + gedit_show_preferences_dialog (window); + } + + void + _gedit_cmd_edit_overwrite_mode (GSimpleAction *action, + GVariant *state, + gpointer user_data) + { + GeditWindow *window = GEDIT_WINDOW (user_data); + GeditView *active_view; + gboolean overwrite; + + gedit_debug (DEBUG_COMMANDS); + + active_view = gedit_window_get_active_view (window); + g_return_if_fail (active_view); + +diff --git a/gedit/gedit-commands-file.c b/gedit/gedit-commands-file.c +index fb64f880d..d1d440a3e 100644 +--- a/gedit/gedit-commands-file.c ++++ b/gedit/gedit-commands-file.c +@@ -1,80 +1,75 @@ + /* + * gedit-commands-file.c + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi + * Copyright (C) 2002-2005 Paolo Maggi + * Copyright (C) 2014 Sébastien Wilmet + * + * 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, see . + */ + + #include "config.h" + + #include "gedit-commands.h" + #include "gedit-commands-private.h" + + #include +-#include + + #include "gedit-app.h" + #include "gedit-debug.h" + #include "gedit-document.h" + #include "gedit-document-private.h" + #include "gedit-tab.h" + #include "gedit-tab-private.h" + #include "gedit-window.h" + #include "gedit-window-private.h" + #include "gedit-notebook.h" + #include "gedit-statusbar.h" + #include "gedit-utils.h" + #include "gedit-file-chooser-dialog.h" + #include "gedit-file-chooser-open.h" + #include "gedit-close-confirmation-dialog.h" + +-/* useful macro */ +-#define GBOOLEAN_TO_POINTER(i) (GINT_TO_POINTER ((i) ? 2 : 1)) +-#define GPOINTER_TO_BOOLEAN(i) ((gboolean) ((GPOINTER_TO_INT(i) == 2) ? TRUE : FALSE)) +- + #define GEDIT_IS_CLOSING_ALL "gedit-is-closing-all" + #define GEDIT_NOTEBOOK_TO_CLOSE "gedit-notebook-to-close" + #define GEDIT_IS_QUITTING "gedit-is-quitting" + #define GEDIT_IS_QUITTING_ALL "gedit-is-quitting-all" + + void + _gedit_cmd_file_new (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) + { + GeditWindow *window = GEDIT_WINDOW (user_data); + + gedit_debug (DEBUG_COMMANDS); + + gedit_window_create_tab (window, TRUE); + } + + static GeditTab * + get_tab_from_file (GList *docs, + GFile *file) + { + GList *l; + + for (l = docs; l != NULL; l = l->next) + { + GeditDocument *doc; + GtkSourceFile *source_file; + GFile *location; + + doc = l->data; +@@ -123,105 +118,107 @@ load_file_list (GeditWindow *window, + gboolean jump_to = TRUE; /* Whether to jump to the new tab */ + const GSList *l; + gint num_loaded_files = 0; + + gedit_debug (DEBUG_COMMANDS); + + win_docs = gedit_window_get_documents (window); + + /* Remove the files corresponding to documents already opened in + * "window" and remove duplicates from the "files" list. + */ + for (l = files; l != NULL; l = l->next) + { + GFile *file = l->data; + + if (is_duplicated_file (files_to_load, file)) + { + continue; + } + + tab = get_tab_from_file (win_docs, file); + + if (tab == NULL) + { + files_to_load = g_slist_prepend (files_to_load, file); + } + else + { + if (l == files) + { +- TeplView *view; ++ GeditDocument *doc; + + gedit_window_set_active_tab (window, tab); + jump_to = FALSE; +- view = TEPL_VIEW (gedit_tab_get_view (tab)); ++ doc = gedit_tab_get_document (tab); + + if (line_pos > 0) + { + if (column_pos > 0) + { +- tepl_view_goto_line_offset (view, +- line_pos - 1, +- column_pos - 1); ++ gedit_document_goto_line_offset (doc, ++ line_pos - 1, ++ column_pos - 1); + } + else + { +- tepl_view_goto_line (view, line_pos - 1); ++ gedit_document_goto_line (doc, line_pos - 1); + } ++ ++ gedit_view_scroll_to_cursor (gedit_tab_get_view (tab)); + } + } + + ++num_loaded_files; + loaded_files = g_slist_prepend (loaded_files, + gedit_tab_get_document (tab)); + } + } + + g_list_free (win_docs); + + if (files_to_load == NULL) + { + return g_slist_reverse (loaded_files); + } + + files_to_load = g_slist_reverse (files_to_load); + l = files_to_load; + + tab = gedit_window_get_active_tab (window); + if (tab != NULL) + { + GeditDocument *doc; + + doc = gedit_tab_get_document (tab); + +- if (tepl_buffer_is_untouched (TEPL_BUFFER (doc)) && ++ if (gedit_document_is_untouched (doc) && + gedit_tab_get_state (tab) == GEDIT_TAB_STATE_NORMAL) + { + _gedit_tab_load (tab, + l->data, + encoding, + line_pos, + column_pos, + create); + + /* make sure the view has focus */ + gtk_widget_grab_focus (GTK_WIDGET (gedit_tab_get_view (tab))); + + l = g_slist_next (l); + jump_to = FALSE; + + ++num_loaded_files; + loaded_files = g_slist_prepend (loaded_files, + gedit_tab_get_document (tab)); + } + } + + while (l != NULL) + { + g_return_val_if_fail (l->data != NULL, NULL); + + tab = gedit_window_create_tab_from_location (window, + l->data, + encoding, + line_pos, + column_pos, +@@ -446,113 +443,113 @@ _gedit_cmd_file_reopen_closed_tab (GSimpleAction *action, + GFile *file; + + file = _gedit_window_pop_last_closed_doc (window); + if (file != NULL) + { + gedit_commands_load_location (window, file, NULL, 0, 0); + } + } + + /* File saving */ + + /* FIXME: modify this dialog to be similar to the one provided by gtk+ for + * already existing files - Paolo (Oct. 11, 2005) */ + static gboolean + replace_read_only_file (GtkWindow *parent, + GFile *file) + { + GtkWidget *dialog; + gint ret; + gchar *parse_name; + gchar *name_for_display; + + gedit_debug (DEBUG_COMMANDS); + + parse_name = g_file_get_parse_name (file); + + /* Truncate the name so it doesn't get insanely wide. Note that even + * though the dialog uses wrapped text, if the name doesn't contain + * white space then the text-wrapping code is too stupid to wrap it. + */ +- name_for_display = tepl_utils_str_middle_truncate (parse_name, 50); ++ name_for_display = gedit_utils_str_middle_truncate (parse_name, 50); + g_free (parse_name); + + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + _("The file “%s” is read-only."), + name_for_display); + g_free (name_for_display); + + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("Do you want to try to replace it " + "with the one you are saving?")); + + gtk_dialog_add_buttons (GTK_DIALOG (dialog), + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Replace"), GTK_RESPONSE_YES, + NULL); + + gtk_dialog_set_default_response (GTK_DIALOG (dialog), + GTK_RESPONSE_CANCEL); + + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + + ret = gtk_dialog_run (GTK_DIALOG (dialog)); + + gtk_widget_destroy (dialog); + + return ret == GTK_RESPONSE_YES; + } + + static gboolean + change_compression (GtkWindow *parent, + GFile *file, + gboolean compressed) + { + GtkWidget *dialog; + gint ret; + gchar *parse_name; + gchar *name_for_display; + const gchar *primary_message; + const gchar *button_label; + + gedit_debug (DEBUG_COMMANDS); + + parse_name = g_file_get_parse_name (file); + + /* Truncate the name so it doesn't get insanely wide. Note that even + * though the dialog uses wrapped text, if the name doesn't contain + * white space then the text-wrapping code is too stupid to wrap it. + */ +- name_for_display = tepl_utils_str_middle_truncate (parse_name, 50); ++ name_for_display = gedit_utils_str_middle_truncate (parse_name, 50); + g_free (parse_name); + + if (compressed) + { + primary_message = _("Save the file using compression?"); + } + else + { + primary_message = _("Save the file as plain text?"); + } + + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + "%s", + primary_message); + + if (compressed) + { + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("The file “%s” was previously saved as plain " + "text and will now be saved using compression."), + name_for_display); + + button_label = _("_Save Using Compression"); + } + else + { + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), +@@ -760,60 +757,61 @@ confirm_overwrite_callback (GeditFileChooserDialog *dialog, + + /* Call save_as_tab_finish() in @callback. */ + static void + save_as_tab_async (GeditTab *tab, + GeditWindow *window, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) + { + GTask *task; + GeditFileChooserDialog *save_dialog; + GtkWindowGroup *window_group; + GtkWindow *dialog_window; + GeditDocument *doc; + GtkSourceFile *file; + GFile *location; + const GtkSourceEncoding *encoding; + GtkSourceNewlineType newline_type; + + g_return_if_fail (GEDIT_IS_TAB (tab)); + g_return_if_fail (GEDIT_IS_WINDOW (window)); + + gedit_debug (DEBUG_COMMANDS); + + task = g_task_new (tab, cancellable, callback, user_data); + g_task_set_task_data (task, g_object_ref (window), g_object_unref); + + /* Translators: "Save As" is the title of the file chooser window. */ + save_dialog = gedit_file_chooser_dialog_create (C_("window title", "Save As"), + GTK_WINDOW (window), ++ GEDIT_FILE_CHOOSER_FLAG_SAVE, + _("_Save"), + _("_Cancel")); + + gedit_file_chooser_dialog_set_do_overwrite_confirmation (save_dialog, TRUE); + + g_signal_connect (save_dialog, + "confirm-overwrite", + G_CALLBACK (confirm_overwrite_callback), + NULL); + + window_group = gedit_window_get_group (window); + + dialog_window = gedit_file_chooser_dialog_get_window (save_dialog); + + if (dialog_window != NULL) + { + gtk_window_group_add_window (window_group, dialog_window); + } + + /* Save As dialog is modal to its main window */ + gedit_file_chooser_dialog_set_modal (save_dialog, TRUE); + + /* Set the suggested file name */ + doc = gedit_tab_get_document (tab); + file = gedit_document_get_file (doc); + location = gtk_source_file_get_location (file); + + if (location != NULL) + { + gedit_file_chooser_dialog_set_file (save_dialog, location); +diff --git a/gedit/gedit-commands-search.c b/gedit/gedit-commands-search.c +index 22703985e..f120b8bad 100644 +--- a/gedit/gedit-commands-search.c ++++ b/gedit/gedit-commands-search.c +@@ -2,61 +2,60 @@ + * gedit-commands-search.c + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi + * Copyright (C) 2002-2006 Paolo Maggi + * Copyright (C) 2013 Sébastien Wilmet + * + * 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, see . + */ + + #include "config.h" + + #include "gedit-commands.h" + #include "gedit-commands-private.h" + + #include + #include + #include +-#include + + #include "gedit-debug.h" + #include "gedit-statusbar.h" + #include "gedit-tab.h" + #include "gedit-tab-private.h" + #include "gedit-view-frame.h" + #include "gedit-window.h" + #include "gedit-window-private.h" + #include "gedit-utils.h" + #include "gedit-replace-dialog.h" + + #define GEDIT_REPLACE_DIALOG_KEY "gedit-replace-dialog-key" + #define GEDIT_LAST_SEARCH_DATA_KEY "gedit-last-search-data-key" + + typedef struct _LastSearchData LastSearchData; + struct _LastSearchData + { + gint x; + gint y; + }; + + static void + last_search_data_free (LastSearchData *data) + { + g_slice_free (LastSearchData, data); + } + + static void + last_search_data_restore_position (GeditReplaceDialog *dlg) + { +@@ -105,117 +104,117 @@ text_found (GeditWindow *window, + window->priv->generic_message_cid, + ngettext("Found and replaced %d occurrence", + "Found and replaced %d occurrences", + occurrences), + occurrences); + } + else if (occurrences == 1) + { + gedit_statusbar_flash_message (GEDIT_STATUSBAR (window->priv->statusbar), + window->priv->generic_message_cid, + _("Found and replaced one occurrence")); + } + else + { + gedit_statusbar_flash_message (GEDIT_STATUSBAR (window->priv->statusbar), + window->priv->generic_message_cid, + " "); + } + } + + #define MAX_MSG_LENGTH 40 + + static void + text_not_found (GeditWindow *window, + GeditReplaceDialog *replace_dialog) + { + const gchar *search_text; + gchar *truncated_text; + + search_text = gedit_replace_dialog_get_search_text (replace_dialog); +- truncated_text = tepl_utils_str_end_truncate (search_text, MAX_MSG_LENGTH); ++ truncated_text = gedit_utils_str_end_truncate (search_text, MAX_MSG_LENGTH); + + gedit_statusbar_flash_message (GEDIT_STATUSBAR (window->priv->statusbar), + window->priv->generic_message_cid, + /* Translators: %s is replaced by the text + entered by the user in the search box */ + _("“%s” not found"), truncated_text); + + g_free (truncated_text); + } + + static void + finish_search_from_dialog (GeditWindow *window, + gboolean found) + { + GeditReplaceDialog *replace_dialog; + + replace_dialog = g_object_get_data (G_OBJECT (window), GEDIT_REPLACE_DIALOG_KEY); + + g_return_if_fail (replace_dialog != NULL); + + if (found) + { + text_found (window, 0); + } + else + { + text_not_found (window, replace_dialog); + } + } + + static gboolean + forward_search_finished (GtkSourceSearchContext *search_context, + GAsyncResult *result, + GeditView *view) + { + gboolean found; + GtkSourceBuffer *buffer; + GtkTextIter match_start; + GtkTextIter match_end; + + found = gtk_source_search_context_forward_finish (search_context, + result, + &match_start, + &match_end, + NULL, + NULL); + + buffer = gtk_source_search_context_get_buffer (search_context); + + if (found) + { + gtk_text_buffer_select_range (GTK_TEXT_BUFFER (buffer), + &match_start, + &match_end); + +- tepl_view_scroll_to_cursor (TEPL_VIEW (view)); ++ gedit_view_scroll_to_cursor (view); + } + else + { + GtkTextIter end_selection; + + gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (buffer), + NULL, + &end_selection); + + gtk_text_buffer_select_range (GTK_TEXT_BUFFER (buffer), + &end_selection, + &end_selection); + } + + return found; + } + + static void + forward_search_from_dialog_finished (GtkSourceSearchContext *search_context, + GAsyncResult *result, + GeditWindow *window) + { + GeditView *view = gedit_window_get_active_view (window); + gboolean found; + + if (view == NULL) + { + return; + } + +@@ -267,61 +266,61 @@ run_forward_search (GeditWindow *window, + (GAsyncReadyCallback)forward_search_finished, + view); + } + } + + static gboolean + backward_search_finished (GtkSourceSearchContext *search_context, + GAsyncResult *result, + GeditView *view) + { + gboolean found; + GtkTextIter match_start; + GtkTextIter match_end; + GtkSourceBuffer *buffer; + + found = gtk_source_search_context_backward_finish (search_context, + result, + &match_start, + &match_end, + NULL, + NULL); + + buffer = gtk_source_search_context_get_buffer (search_context); + + if (found) + { + gtk_text_buffer_select_range (GTK_TEXT_BUFFER (buffer), + &match_start, + &match_end); + +- tepl_view_scroll_to_cursor (TEPL_VIEW (view)); ++ gedit_view_scroll_to_cursor (view); + } + else + { + GtkTextIter start_selection; + + gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (buffer), + &start_selection, + NULL); + + gtk_text_buffer_select_range (GTK_TEXT_BUFFER (buffer), + &start_selection, + &start_selection); + } + + return found; + } + + static void + backward_search_from_dialog_finished (GtkSourceSearchContext *search_context, + GAsyncResult *result, + GeditWindow *window) + { + GeditView *view = gedit_window_get_active_view (window); + gboolean found; + + if (view == NULL) + { + return; + } + +diff --git a/gedit/gedit-commands-view.c b/gedit/gedit-commands-view.c +index 71785b78f..369bc93b0 100644 +--- a/gedit/gedit-commands-view.c ++++ b/gedit/gedit-commands-view.c +@@ -1,58 +1,63 @@ + /* + * gedit-view-commands.c + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi + * Copyright (C) 2002-2005 Paolo Maggi + * + * 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, see . + */ + + #include "config.h" ++ + #include "gedit-commands.h" + #include "gedit-commands-private.h" +-#include ++ ++#include ++ + #include "gedit-debug.h" + #include "gedit-window.h" ++#include "gedit-highlight-mode-dialog.h" ++#include "gedit-highlight-mode-selector.h" + + void + _gedit_cmd_view_focus_active (GSimpleAction *action, + GVariant *state, + gpointer user_data) + { + GeditView *active_view; + GeditWindow *window = GEDIT_WINDOW (user_data); + + gedit_debug (DEBUG_COMMANDS); + + active_view = gedit_window_get_active_view (window); + + if (active_view) + { + gtk_widget_grab_focus (GTK_WIDGET (active_view)); + } + } + + void + _gedit_cmd_view_toggle_side_panel (GSimpleAction *action, + GVariant *state, + gpointer user_data) + { + GeditWindow *window = GEDIT_WINDOW (user_data); + GtkWidget *panel; + gboolean visible; + + gedit_debug (DEBUG_COMMANDS); + +@@ -94,88 +99,75 @@ _gedit_cmd_view_toggle_bottom_panel (GSimpleAction *action, + } + + void + _gedit_cmd_view_toggle_fullscreen_mode (GSimpleAction *action, + GVariant *state, + gpointer user_data) + { + GeditWindow *window = GEDIT_WINDOW (user_data); + + gedit_debug (DEBUG_COMMANDS); + + if (g_variant_get_boolean (state)) + { + _gedit_window_fullscreen (window); + } + else + { + _gedit_window_unfullscreen (window); + } + } + + void + _gedit_cmd_view_leave_fullscreen_mode (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) + { + _gedit_window_unfullscreen (GEDIT_WINDOW (user_data)); + } + + static void +-language_activated_cb (TeplLanguageChooserDialog *dialog, +- GtkSourceLanguage *language, +- GeditWindow *window) ++language_selected_cb (GeditHighlightModeSelector *selector, ++ GtkSourceLanguage *language, ++ GeditWindow *window) + { + GeditDocument *active_document; + + active_document = gedit_window_get_active_document (window); + if (active_document != NULL) + { + gedit_document_set_language (active_document, language); + } +- +- gtk_widget_destroy (GTK_WIDGET (dialog)); +-} +- +-static void +-language_chooser_dialog_response_after_cb (TeplLanguageChooserDialog *dialog, +- gint response_id, +- gpointer user_data) +-{ +- gtk_widget_destroy (GTK_WIDGET (dialog)); + } + + void + _gedit_cmd_view_highlight_mode (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) + { + GeditWindow *window = GEDIT_WINDOW (user_data); +- TeplLanguageChooserDialog *dialog; ++ GeditHighlightModeDialog *dialog; ++ GeditHighlightModeSelector *selector; + GeditDocument *active_document; + +- dialog = tepl_language_chooser_dialog_new (GTK_WINDOW (window)); ++ dialog = GEDIT_HIGHLIGHT_MODE_DIALOG (gedit_highlight_mode_dialog_new (GTK_WINDOW (window))); ++ selector = gedit_highlight_mode_dialog_get_selector (dialog); + + active_document = gedit_window_get_active_document (window); + if (active_document != NULL) + { + GtkSourceLanguage *language; + + language = gedit_document_get_language (active_document); +- tepl_language_chooser_select_language (TEPL_LANGUAGE_CHOOSER (dialog), language); ++ gedit_highlight_mode_selector_select_language (selector, language); + } + +- g_signal_connect_object (dialog, +- "language-activated", +- G_CALLBACK (language_activated_cb), ++ g_signal_connect_object (selector, ++ "language-selected", ++ G_CALLBACK (language_selected_cb), + window, + 0); + +- g_signal_connect_after (dialog, +- "response", +- G_CALLBACK (language_chooser_dialog_response_after_cb), +- NULL); +- + gtk_widget_show (GTK_WIDGET (dialog)); + } + + /* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-debug.c b/gedit/gedit-debug.c +index 5aa82fa51..396dc140f 100644 +--- a/gedit/gedit-debug.c ++++ b/gedit/gedit-debug.c +@@ -73,60 +73,64 @@ gedit_debug_init (void) + enabled_sections |= GEDIT_DEBUG_WINDOW; + } + if (g_getenv ("GEDIT_DEBUG_PANEL") != NULL) + { + enabled_sections |= GEDIT_DEBUG_PANEL; + } + if (g_getenv ("GEDIT_DEBUG_PLUGINS") != NULL) + { + enabled_sections |= GEDIT_DEBUG_PLUGINS; + } + if (g_getenv ("GEDIT_DEBUG_TAB") != NULL) + { + enabled_sections |= GEDIT_DEBUG_TAB; + } + if (g_getenv ("GEDIT_DEBUG_DOCUMENT") != NULL) + { + enabled_sections |= GEDIT_DEBUG_DOCUMENT; + } + if (g_getenv ("GEDIT_DEBUG_COMMANDS") != NULL) + { + enabled_sections |= GEDIT_DEBUG_COMMANDS; + } + if (g_getenv ("GEDIT_DEBUG_APP") != NULL) + { + enabled_sections |= GEDIT_DEBUG_APP; + } + if (g_getenv ("GEDIT_DEBUG_UTILS") != NULL) + { + enabled_sections |= GEDIT_DEBUG_UTILS; + } ++ if (g_getenv ("GEDIT_DEBUG_METADATA") != NULL) ++ { ++ enabled_sections |= GEDIT_DEBUG_METADATA; ++ } + + out: + + #ifdef ENABLE_PROFILING + if (enabled_sections != GEDIT_NO_DEBUG) + { + timer = g_timer_new (); + } + #endif + } + + /** + * gedit_debug: + * @section: debug section. + * @file: file name. + * @line: line number. + * @function: name of the function that is calling gedit_debug(). + * + * If @section is enabled, then logs the trace information @file, @line, and + * @function. + */ + void + gedit_debug (GeditDebugSection section, + const gchar *file, + gint line, + const gchar *function) + { + gedit_debug_message (section, file, line, function, "%s", ""); + } + +diff --git a/gedit/gedit-debug.h b/gedit/gedit-debug.h +index a9d7caf37..49e5127e1 100644 +--- a/gedit/gedit-debug.h ++++ b/gedit/gedit-debug.h +@@ -21,63 +21,65 @@ + */ + + #ifndef GEDIT_DEBUG_H + #define GEDIT_DEBUG_H + + #include + + /** + * GeditDebugSection: + * + * Enumeration of debug sections. + * + * Debugging output for a section is enabled by setting an environment variable + * of the same name. For example, setting the GEDIT_DEBUG_PLUGINS + * environment variable enables all debugging output for the %GEDIT_DEBUG_PLUGINS + * section. Setting the special environment variable GEDIT_DEBUG + * enables output for all sections. + */ + typedef enum { + GEDIT_NO_DEBUG = 0, + GEDIT_DEBUG_VIEW = 1 << 0, + GEDIT_DEBUG_PREFS = 1 << 1, + GEDIT_DEBUG_WINDOW = 1 << 2, + GEDIT_DEBUG_PANEL = 1 << 3, + GEDIT_DEBUG_PLUGINS = 1 << 4, + GEDIT_DEBUG_TAB = 1 << 5, + GEDIT_DEBUG_DOCUMENT = 1 << 6, + GEDIT_DEBUG_COMMANDS = 1 << 7, + GEDIT_DEBUG_APP = 1 << 8, + GEDIT_DEBUG_UTILS = 1 << 9, ++ GEDIT_DEBUG_METADATA = 1 << 10, + } GeditDebugSection; + + #define DEBUG_VIEW GEDIT_DEBUG_VIEW, __FILE__, __LINE__, G_STRFUNC + #define DEBUG_PREFS GEDIT_DEBUG_PREFS, __FILE__, __LINE__, G_STRFUNC + #define DEBUG_WINDOW GEDIT_DEBUG_WINDOW, __FILE__, __LINE__, G_STRFUNC + #define DEBUG_PANEL GEDIT_DEBUG_PANEL, __FILE__, __LINE__, G_STRFUNC + #define DEBUG_PLUGINS GEDIT_DEBUG_PLUGINS, __FILE__, __LINE__, G_STRFUNC + #define DEBUG_TAB GEDIT_DEBUG_TAB, __FILE__, __LINE__, G_STRFUNC + #define DEBUG_DOCUMENT GEDIT_DEBUG_DOCUMENT,__FILE__, __LINE__, G_STRFUNC + #define DEBUG_COMMANDS GEDIT_DEBUG_COMMANDS,__FILE__, __LINE__, G_STRFUNC + #define DEBUG_APP GEDIT_DEBUG_APP, __FILE__, __LINE__, G_STRFUNC + #define DEBUG_UTILS GEDIT_DEBUG_UTILS, __FILE__, __LINE__, G_STRFUNC ++#define DEBUG_METADATA GEDIT_DEBUG_METADATA,__FILE__, __LINE__, G_STRFUNC + + void gedit_debug_init (void); + + void gedit_debug (GeditDebugSection section, + const gchar *file, + gint line, + const gchar *function); + + void gedit_debug_message (GeditDebugSection section, + const gchar *file, + gint line, + const gchar *function, + const gchar *format, ...) G_GNUC_PRINTF(5, 6); + + void gedit_debug_plugin_message (const gchar *file, + gint line, + const gchar *function, + const gchar *message); + + #endif /* GEDIT_DEBUG_H */ + /* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-document-private.h b/gedit/gedit-document-private.h +index 62444cc76..574c0bb89 100644 +--- a/gedit/gedit-document-private.h ++++ b/gedit/gedit-document-private.h +@@ -1,57 +1,63 @@ + /* + * gedit-document.h + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi + * Copyright (C) 2002-2005 Paolo Maggi + * Copyright (C) 2014, 2020 Sébastien Wilmet + * + * 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, see . + */ + + #ifndef GEDIT_DOCUMENT_PRIVATE_H + #define GEDIT_DOCUMENT_PRIVATE_H + + #include "gedit-document.h" + + G_BEGIN_DECLS + +-#define GEDIT_METADATA_ATTRIBUTE_POSITION "gedit-position" +-#define GEDIT_METADATA_ATTRIBUTE_ENCODING "gedit-encoding" +-#define GEDIT_METADATA_ATTRIBUTE_LANGUAGE "gedit-language" ++#ifdef G_OS_WIN32 ++#define GEDIT_METADATA_ATTRIBUTE_POSITION "position" ++#define GEDIT_METADATA_ATTRIBUTE_ENCODING "encoding" ++#define GEDIT_METADATA_ATTRIBUTE_LANGUAGE "language" ++#else ++#define GEDIT_METADATA_ATTRIBUTE_POSITION "metadata::gedit-position" ++#define GEDIT_METADATA_ATTRIBUTE_ENCODING "metadata::gedit-encoding" ++#define GEDIT_METADATA_ATTRIBUTE_LANGUAGE "metadata::gedit-language" ++#endif + + G_GNUC_INTERNAL + glong _gedit_document_get_seconds_since_last_save_or_load (GeditDocument *doc); + + G_GNUC_INTERNAL + gboolean _gedit_document_needs_saving (GeditDocument *doc); + + G_GNUC_INTERNAL + gboolean _gedit_document_get_empty_search (GeditDocument *doc); + + G_GNUC_INTERNAL + void _gedit_document_set_create (GeditDocument *doc, + gboolean create); + + G_GNUC_INTERNAL + gboolean _gedit_document_get_create (GeditDocument *doc); + + G_GNUC_INTERNAL + gchar * _gedit_document_get_uri_for_display (GeditDocument *doc); + + G_END_DECLS + + #endif /* GEDIT_DOCUMENT_PRIVATE_H */ + /* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-document.c b/gedit/gedit-document.c +index 54c11a96c..4cb08ee03 100644 +--- a/gedit/gedit-document.c ++++ b/gedit/gedit-document.c +@@ -1,156 +1,177 @@ + /* + * gedit-document.c + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi + * Copyright (C) 2002-2005 Paolo Maggi +- * Copyright (C) 2014-2020 Sébastien Wilmet ++ * Copyright (C) 2014-2015 Sébastien Wilmet + * + * 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, see . + */ + + #include "config.h" ++ + #include "gedit-document.h" + #include "gedit-document-private.h" ++ + #include + #include ++ ++#include "gedit-app.h" ++#include "gedit-app-private.h" + #include "gedit-settings.h" + #include "gedit-debug.h" + #include "gedit-utils.h" ++#include "gedit-metadata-manager.h" ++ ++#define METADATA_QUERY "metadata::*" + + #define NO_LANGUAGE_NAME "_NORMAL_" + + static void gedit_document_loaded_real (GeditDocument *doc); + + static void gedit_document_saved_real (GeditDocument *doc); + + static void set_content_type (GeditDocument *doc, + const gchar *content_type); + + typedef struct + { + GtkSourceFile *file; + +- TeplMetadata *metadata; ++ GSettings *editor_settings; ++ ++ gint untitled_number; ++ ++ GFileInfo *metadata_info; + + gchar *content_type; + + GDateTime *time_of_last_save_or_load; + + /* The search context for the incremental search, or the search and + * replace. They are mutually exclusive. + */ + GtkSourceSearchContext *search_context; + ++ GeditMetadataManager *metadata_manager; ++ ++ guint user_action; ++ + guint language_set_by_user : 1; ++ guint use_gvfs_metadata : 1; + + /* The search is empty if there is no search context, or if the + * search text is empty. It is used for the sensitivity of some menu + * actions. + */ + guint empty_search : 1; + + /* Create file if location points to a non existing file (for example + * when opened from the command line). + */ + guint create : 1; + } GeditDocumentPrivate; + + enum + { + PROP_0, ++ PROP_SHORTNAME, + PROP_CONTENT_TYPE, + PROP_MIME_TYPE, + PROP_EMPTY_SEARCH, ++ PROP_USE_GVFS_METADATA, + LAST_PROP + }; + + static GParamSpec *properties[LAST_PROP]; + + enum + { ++ CURSOR_MOVED, + LOAD, + LOADED, + SAVE, + SAVED, + LAST_SIGNAL + }; + + static guint document_signals[LAST_SIGNAL]; + +-G_DEFINE_TYPE_WITH_PRIVATE (GeditDocument, gedit_document, TEPL_TYPE_BUFFER) ++static GHashTable *allocated_untitled_numbers = NULL; + +-static void +-load_metadata_from_metadata_manager (GeditDocument *doc) ++G_DEFINE_TYPE_WITH_PRIVATE (GeditDocument, gedit_document, GTK_SOURCE_TYPE_BUFFER) ++ ++static gint ++get_untitled_number (void) + { +- GeditDocumentPrivate *priv = gedit_document_get_instance_private (doc); +- GFile *location; ++ gint i = 1; + +- location = gtk_source_file_get_location (priv->file); ++ if (allocated_untitled_numbers == NULL) ++ allocated_untitled_numbers = g_hash_table_new (NULL, NULL); + +- if (location != NULL) ++ g_return_val_if_fail (allocated_untitled_numbers != NULL, -1); ++ ++ while (TRUE) + { +- TeplMetadataManager *manager; ++ if (g_hash_table_lookup (allocated_untitled_numbers, GINT_TO_POINTER (i)) == NULL) ++ { ++ g_hash_table_insert (allocated_untitled_numbers, ++ GINT_TO_POINTER (i), ++ GINT_TO_POINTER (i)); + +- manager = tepl_metadata_manager_get_singleton (); +- tepl_metadata_manager_copy_from (manager, location, priv->metadata); ++ return i; ++ } ++ ++ ++i; + } + } + + static void +-save_metadata_into_metadata_manager (GeditDocument *doc) ++release_untitled_number (gint n) + { +- GeditDocumentPrivate *priv = gedit_document_get_instance_private (doc); +- GFile *location; +- +- location = gtk_source_file_get_location (priv->file); +- +- if (location != NULL) +- { +- TeplMetadataManager *manager; ++ g_return_if_fail (allocated_untitled_numbers != NULL); + +- manager = tepl_metadata_manager_get_singleton (); +- tepl_metadata_manager_merge_into (manager, location, priv->metadata); +- } ++ g_hash_table_remove (allocated_untitled_numbers, GINT_TO_POINTER (n)); + } + + static void + update_time_of_last_save_or_load (GeditDocument *doc) + { + GeditDocumentPrivate *priv = gedit_document_get_instance_private (doc); + + if (priv->time_of_last_save_or_load != NULL) + { + g_date_time_unref (priv->time_of_last_save_or_load); + } + + priv->time_of_last_save_or_load = g_date_time_new_now_utc (); + } + + static const gchar * + get_language_string (GeditDocument *doc) + { + GtkSourceLanguage *lang = gedit_document_get_language (doc); + + return lang != NULL ? gtk_source_language_get_id (lang) : NO_LANGUAGE_NAME; + } + + static void + save_metadata (GeditDocument *doc) + { + GeditDocumentPrivate *priv; + const gchar *language = NULL; + GtkTextIter iter; + gchar *position; +@@ -168,215 +189,361 @@ save_metadata (GeditDocument *doc) + position = g_strdup_printf ("%d", gtk_text_iter_get_offset (&iter)); + + if (language == NULL) + { + gedit_document_set_metadata (doc, + GEDIT_METADATA_ATTRIBUTE_POSITION, position, + NULL); + } + else + { + gedit_document_set_metadata (doc, + GEDIT_METADATA_ATTRIBUTE_POSITION, position, + GEDIT_METADATA_ATTRIBUTE_LANGUAGE, language, + NULL); + } + + g_free (position); + } + + static void + gedit_document_dispose (GObject *object) + { + GeditDocument *doc = GEDIT_DOCUMENT (object); + GeditDocumentPrivate *priv = gedit_document_get_instance_private (doc); + + gedit_debug (DEBUG_DOCUMENT); + + /* Metadata must be saved here and not in finalize because the language + * is gone by the time finalize runs. + */ +- if (priv->metadata != NULL) ++ if (priv->file != NULL) + { + save_metadata (doc); + +- g_object_unref (priv->metadata); +- priv->metadata = NULL; ++ g_object_unref (priv->file); ++ priv->file = NULL; + } + +- g_clear_object (&priv->file); ++ g_clear_object (&priv->metadata_info); + g_clear_object (&priv->search_context); ++ g_clear_object (&priv->metadata_manager); + + G_OBJECT_CLASS (gedit_document_parent_class)->dispose (object); + } + + static void + gedit_document_finalize (GObject *object) + { +- GeditDocumentPrivate *priv = gedit_document_get_instance_private (GEDIT_DOCUMENT (object)); ++ GeditDocumentPrivate *priv; + + gedit_debug (DEBUG_DOCUMENT); + ++ priv = gedit_document_get_instance_private (GEDIT_DOCUMENT (object)); ++ ++ if (priv->untitled_number > 0) ++ { ++ release_untitled_number (priv->untitled_number); ++ } ++ + g_free (priv->content_type); + + if (priv->time_of_last_save_or_load != NULL) + { + g_date_time_unref (priv->time_of_last_save_or_load); + } + + G_OBJECT_CLASS (gedit_document_parent_class)->finalize (object); + } + + static void + gedit_document_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) + { + GeditDocument *doc = GEDIT_DOCUMENT (object); + GeditDocumentPrivate *priv; + + priv = gedit_document_get_instance_private (doc); + + switch (prop_id) + { ++ case PROP_SHORTNAME: ++ g_value_take_string (value, gedit_document_get_short_name_for_display (doc)); ++ break; ++ + case PROP_CONTENT_TYPE: + g_value_take_string (value, gedit_document_get_content_type (doc)); + break; + + case PROP_MIME_TYPE: + g_value_take_string (value, gedit_document_get_mime_type (doc)); + break; + + case PROP_EMPTY_SEARCH: + g_value_set_boolean (value, priv->empty_search); + break; + ++ case PROP_USE_GVFS_METADATA: ++ g_value_set_boolean (value, priv->use_gvfs_metadata); ++ break; ++ + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + } + + static void + gedit_document_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) + { + GeditDocument *doc = GEDIT_DOCUMENT (object); ++ GeditDocumentPrivate *priv = gedit_document_get_instance_private (doc); + + switch (prop_id) + { + case PROP_CONTENT_TYPE: + set_content_type (doc, g_value_get_string (value)); + break; + ++ case PROP_USE_GVFS_METADATA: ++ priv->use_gvfs_metadata = g_value_get_boolean (value); ++ break; ++ + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + } + ++static void ++gedit_document_begin_user_action (GtkTextBuffer *buffer) ++{ ++ GeditDocumentPrivate *priv; ++ ++ priv = gedit_document_get_instance_private (GEDIT_DOCUMENT (buffer)); ++ ++ ++priv->user_action; ++ ++ if (GTK_TEXT_BUFFER_CLASS (gedit_document_parent_class)->begin_user_action != NULL) ++ { ++ GTK_TEXT_BUFFER_CLASS (gedit_document_parent_class)->begin_user_action (buffer); ++ } ++} ++ ++static void ++gedit_document_end_user_action (GtkTextBuffer *buffer) ++{ ++ GeditDocumentPrivate *priv; ++ ++ priv = gedit_document_get_instance_private (GEDIT_DOCUMENT (buffer)); ++ ++ --priv->user_action; ++ ++ if (GTK_TEXT_BUFFER_CLASS (gedit_document_parent_class)->end_user_action != NULL) ++ { ++ GTK_TEXT_BUFFER_CLASS (gedit_document_parent_class)->end_user_action (buffer); ++ } ++} ++ ++static void ++gedit_document_mark_set (GtkTextBuffer *buffer, ++ const GtkTextIter *iter, ++ GtkTextMark *mark) ++{ ++ GeditDocument *doc = GEDIT_DOCUMENT (buffer); ++ GeditDocumentPrivate *priv; ++ ++ priv = gedit_document_get_instance_private (doc); ++ ++ if (GTK_TEXT_BUFFER_CLASS (gedit_document_parent_class)->mark_set != NULL) ++ { ++ GTK_TEXT_BUFFER_CLASS (gedit_document_parent_class)->mark_set (buffer, iter, mark); ++ } ++ ++ if (mark == gtk_text_buffer_get_insert (buffer) && (priv->user_action == 0)) ++ { ++ g_signal_emit (doc, document_signals[CURSOR_MOVED], 0); ++ } ++} ++ ++static void ++gedit_document_changed (GtkTextBuffer *buffer) ++{ ++ g_signal_emit (GEDIT_DOCUMENT (buffer), document_signals[CURSOR_MOVED], 0); ++ ++ GTK_TEXT_BUFFER_CLASS (gedit_document_parent_class)->changed (buffer); ++} ++ + static void + gedit_document_constructed (GObject *object) + { + GeditDocument *doc = GEDIT_DOCUMENT (object); ++ GeditDocumentPrivate *priv; + GeditSettings *settings; + GSettings *editor_settings; + ++ priv = gedit_document_get_instance_private (doc); ++ + settings = _gedit_settings_get_singleton (); + editor_settings = _gedit_settings_peek_editor_settings (settings); + ++ if (!priv->use_gvfs_metadata) ++ { ++ GeditMetadataManager *metadata_manager; ++ ++ metadata_manager = _gedit_app_get_metadata_manager (GEDIT_APP (g_application_get_default ())); ++ g_assert (GEDIT_IS_METADATA_MANAGER (metadata_manager)); ++ priv->metadata_manager = g_object_ref (metadata_manager); ++ } ++ + /* Bind construct properties. */ + g_settings_bind (editor_settings, GEDIT_SETTINGS_ENSURE_TRAILING_NEWLINE, + doc, "implicit-trailing-newline", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_NO_SENSITIVITY); + + G_OBJECT_CLASS (gedit_document_parent_class)->constructed (object); + } + + static void + gedit_document_class_init (GeditDocumentClass *klass) + { + GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ GtkTextBufferClass *buf_class = GTK_TEXT_BUFFER_CLASS (klass); + + object_class->dispose = gedit_document_dispose; + object_class->finalize = gedit_document_finalize; + object_class->get_property = gedit_document_get_property; + object_class->set_property = gedit_document_set_property; + object_class->constructed = gedit_document_constructed; + ++ buf_class->begin_user_action = gedit_document_begin_user_action; ++ buf_class->end_user_action = gedit_document_end_user_action; ++ buf_class->mark_set = gedit_document_mark_set; ++ buf_class->changed = gedit_document_changed; ++ + klass->loaded = gedit_document_loaded_real; + klass->saved = gedit_document_saved_real; + ++ /** ++ * GeditDocument:shortname: ++ * ++ * The document's short name. ++ */ ++ properties[PROP_SHORTNAME] = ++ g_param_spec_string ("shortname", ++ "Short Name", ++ "The document's short name", ++ NULL, ++ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); ++ + /** + * GeditDocument:content-type: + * + * The document's content type. + */ + properties[PROP_CONTENT_TYPE] = + g_param_spec_string ("content-type", + "Content Type", + "The document's Content Type", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * GeditDocument:mime-type: + * + * The document's MIME type. + */ + properties[PROP_MIME_TYPE] = + g_param_spec_string ("mime-type", + "MIME Type", + "The document's MIME Type", + "text/plain", + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * GeditDocument:empty-search: + * + * + * The property is used internally by gedit. It must not be used in a + * gedit plugin. The property can be modified or removed at any time. + * + */ + properties[PROP_EMPTY_SEARCH] = + g_param_spec_boolean ("empty-search", + "Empty search", + "Whether the search is empty", + TRUE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + ++ /** ++ * GeditDocument:use-gvfs-metadata: ++ * ++ * Whether to use GVFS metadata. If %FALSE, use the gedit metadata ++ * manager that stores the metadata in an XML file in the user cache ++ * directory. ++ * ++ * ++ * The property is used internally by gedit. It must not be used in a ++ * gedit plugin. The property can be modified or removed at any time. ++ * ++ */ ++ properties[PROP_USE_GVFS_METADATA] = ++ g_param_spec_boolean ("use-gvfs-metadata", ++ "Use GVFS metadata", ++ "", ++ TRUE, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); ++ + g_object_class_install_properties (object_class, LAST_PROP, properties); + ++ /* This signal is used to update the cursor position in the statusbar, ++ * it's emitted either when the insert mark is moved explicitely or ++ * when the buffer changes (insert/delete). ++ * FIXME When the replace_all was implemented in gedit, this signal was ++ * not emitted during the replace_all to improve performance. Now the ++ * replace_all is implemented in GtkSourceView, so the signal is ++ * emitted. ++ */ ++ document_signals[CURSOR_MOVED] = ++ g_signal_new ("cursor-moved", ++ G_OBJECT_CLASS_TYPE (object_class), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GeditDocumentClass, cursor_moved), ++ NULL, NULL, NULL, ++ G_TYPE_NONE, ++ 0); ++ + /** + * GeditDocument::load: + * @document: the #GeditDocument. + * + * The "load" signal is emitted at the beginning of a file loading. + * + * Before gedit 3.14 this signal contained parameters to configure the + * file loading (the location, encoding, etc). Plugins should not need + * those parameters. + */ + document_signals[LOAD] = + g_signal_new ("load", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GeditDocumentClass, load), + NULL, NULL, NULL, + G_TYPE_NONE, 0); + + /** + * GeditDocument::loaded: + * @document: the #GeditDocument. + * + * The "loaded" signal is emitted at the end of a successful file + * loading. + * + * Before gedit 3.14 this signal contained a #GError parameter, and the + * signal was also emitted if an error occurred. Plugins should not need + * the error parameter. + */ + document_signals[LOADED] = +@@ -605,128 +772,188 @@ static void + on_content_type_changed (GeditDocument *doc, + GParamSpec *pspec, + gpointer useless) + { + GeditDocumentPrivate *priv; + + priv = gedit_document_get_instance_private (doc); + + if (!priv->language_set_by_user) + { + GtkSourceLanguage *language = guess_language (doc); + + gedit_debug_message (DEBUG_DOCUMENT, "Language: %s", + language != NULL ? gtk_source_language_get_name (language) : "None"); + + set_language (doc, language, FALSE); + } + } + + static gchar * + get_default_content_type (void) + { + return g_content_type_from_mime_type ("text/plain"); + } + + static void + on_location_changed (GtkSourceFile *file, + GParamSpec *pspec, + GeditDocument *doc) + { ++ GeditDocumentPrivate *priv; ++ GFile *location; ++ + gedit_debug (DEBUG_DOCUMENT); +- load_metadata_from_metadata_manager (doc); ++ ++ priv = gedit_document_get_instance_private (doc); ++ ++ location = gtk_source_file_get_location (file); ++ ++ if (location != NULL && priv->untitled_number > 0) ++ { ++ release_untitled_number (priv->untitled_number); ++ priv->untitled_number = 0; ++ } ++ ++ g_object_notify_by_pspec (G_OBJECT (doc), properties[PROP_SHORTNAME]); ++ ++ /* Load metadata for this location: we load sync since metadata is ++ * always local so it should be fast and we need the information ++ * right after the location was set. ++ * TODO: do async I/O for the metadata. ++ */ ++ if (priv->use_gvfs_metadata && location != NULL) ++ { ++ GError *error = NULL; ++ ++ if (priv->metadata_info != NULL) ++ { ++ g_object_unref (priv->metadata_info); ++ } ++ ++ priv->metadata_info = g_file_query_info (location, ++ METADATA_QUERY, ++ G_FILE_QUERY_INFO_NONE, ++ NULL, ++ &error); ++ ++ if (error != NULL) ++ { ++ /* Do not complain about metadata if we are opening a ++ * non existing file. ++ */ ++ if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_ISDIR) && ++ !g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOTDIR) && ++ !g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT) && ++ !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) ++ { ++ g_warning ("%s", error->message); ++ } ++ ++ g_error_free (error); ++ } ++ ++ if (priv->metadata_info == NULL) ++ { ++ priv->metadata_info = g_file_info_new (); ++ } ++ } + } + + static void + gedit_document_init (GeditDocument *doc) + { + GeditDocumentPrivate *priv = gedit_document_get_instance_private (doc); +- TeplFile *tepl_file; + GeditSettings *settings; + GSettings *editor_settings; + + gedit_debug (DEBUG_DOCUMENT); + ++ settings = _gedit_settings_get_singleton (); ++ editor_settings = _gedit_settings_peek_editor_settings (settings); ++ ++ priv->untitled_number = get_untitled_number (); + priv->content_type = get_default_content_type (); + priv->language_set_by_user = FALSE; + priv->empty_search = TRUE; + + update_time_of_last_save_or_load (doc); + + priv->file = gtk_source_file_new (); +- tepl_file = tepl_buffer_get_file (TEPL_BUFFER (doc)); +- +- g_object_bind_property (priv->file, "location", +- tepl_file, "location", +- G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); +- +- priv->metadata = tepl_metadata_new (); ++ priv->metadata_info = g_file_info_new (); + + g_signal_connect_object (priv->file, + "notify::location", + G_CALLBACK (on_location_changed), + doc, + 0); + +- settings = _gedit_settings_get_singleton (); +- editor_settings = _gedit_settings_peek_editor_settings (settings); +- + g_settings_bind (editor_settings, GEDIT_SETTINGS_MAX_UNDO_ACTIONS, + doc, "max-undo-levels", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_NO_SENSITIVITY); + + g_settings_bind (editor_settings, GEDIT_SETTINGS_SYNTAX_HIGHLIGHTING, + doc, "highlight-syntax", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_NO_SENSITIVITY); + + g_settings_bind (editor_settings, GEDIT_SETTINGS_BRACKET_MATCHING, + doc, "highlight-matching-brackets", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_NO_SENSITIVITY); + + g_signal_connect_object (editor_settings, + "changed::" GEDIT_SETTINGS_SCHEME, + G_CALLBACK (editor_settings_scheme_changed_cb), + doc, + 0); + + update_style_scheme (doc); + + g_signal_connect (doc, + "notify::content-type", + G_CALLBACK (on_content_type_changed), + NULL); + } + + GeditDocument * + gedit_document_new (void) + { +- return g_object_new (GEDIT_TYPE_DOCUMENT, NULL); ++ gboolean use_gvfs_metadata; ++ ++#ifdef ENABLE_GVFS_METADATA ++ use_gvfs_metadata = TRUE; ++#else ++ use_gvfs_metadata = FALSE; ++#endif ++ ++ return g_object_new (GEDIT_TYPE_DOCUMENT, ++ "use-gvfs-metadata", use_gvfs_metadata, ++ NULL); + } + + static gchar * + get_content_type_from_content (GeditDocument *doc) + { + gchar *content_type; + gchar *data; + GtkTextBuffer *buffer; + GtkTextIter start; + GtkTextIter end; + + buffer = GTK_TEXT_BUFFER (doc); + + gtk_text_buffer_get_start_iter (buffer, &start); + end = start; + gtk_text_iter_forward_chars (&end, 255); + + data = gtk_text_buffer_get_text (buffer, &start, &end, TRUE); + + content_type = g_content_type_guess (NULL, + (const guchar *)data, + strlen (data), + NULL); + + g_free (data); + + return content_type; + } + + static void +@@ -787,93 +1014,107 @@ set_content_type (GeditDocument *doc, + if (content_type == NULL) + { + GFile *location; + gchar *guessed_type = NULL; + + /* If content type is null, we guess from the filename */ + location = gtk_source_file_get_location (priv->file); + if (location != NULL) + { + gchar *basename; + + basename = g_file_get_basename (location); + guessed_type = g_content_type_guess (basename, NULL, 0, NULL); + + g_free (basename); + } + + set_content_type_no_guess (doc, guessed_type); + g_free (guessed_type); + } + else + { + set_content_type_no_guess (doc, content_type); + } + } + + /* Note: this never returns %NULL. */ + gchar * + _gedit_document_get_uri_for_display (GeditDocument *doc) + { +- TeplFile *file; ++ GeditDocumentPrivate *priv; + GFile *location; + + g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), g_strdup ("")); + +- file = tepl_buffer_get_file (TEPL_BUFFER (doc)); +- location = tepl_file_get_location (file); ++ priv = gedit_document_get_instance_private (doc); ++ ++ location = gtk_source_file_get_location (priv->file); + + if (location == NULL) + { +- return tepl_file_get_short_name (file); ++ return g_strdup_printf (_("Untitled Document %d"), ++ priv->untitled_number); + } + else + { + return g_file_get_parse_name (location); + } + } + + /** + * gedit_document_get_short_name_for_display: + * @doc: a #GeditDocument. + * + * Note: this never returns %NULL. + **/ + gchar * + gedit_document_get_short_name_for_display (GeditDocument *doc) + { +- TeplFile *file; ++ GeditDocumentPrivate *priv; ++ GFile *location; + + g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), g_strdup ("")); + +- file = tepl_buffer_get_file (TEPL_BUFFER (doc)); +- return tepl_file_get_short_name (file); ++ priv = gedit_document_get_instance_private (doc); ++ ++ location = gtk_source_file_get_location (priv->file); ++ ++ if (location == NULL) ++ { ++ return g_strdup_printf (_("Untitled Document %d"), ++ priv->untitled_number); ++ } ++ else ++ { ++ return gedit_utils_basename_for_display (location); ++ } + } + + gchar * + gedit_document_get_content_type (GeditDocument *doc) + { + GeditDocumentPrivate *priv; + + g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), NULL); + + priv = gedit_document_get_instance_private (doc); + + return g_strdup (priv->content_type); + } + + /** + * gedit_document_get_mime_type: + * @doc: a #GeditDocument. + * + * Note: this never returns %NULL. + **/ + gchar * + gedit_document_get_mime_type (GeditDocument *doc) + { + GeditDocumentPrivate *priv; + + g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), g_strdup ("text/plain")); + + priv = gedit_document_get_instance_private (doc); + + if (priv->content_type != NULL && +@@ -1005,225 +1246,398 @@ saved_query_info_cb (GFile *location, + priv->create = FALSE; + + save_encoding_metadata (doc); + + /* Async operation finished. */ + g_object_unref (doc); + } + + static void + gedit_document_saved_real (GeditDocument *doc) + { + GeditDocumentPrivate *priv; + GFile *location; + + priv = gedit_document_get_instance_private (doc); + + location = gtk_source_file_get_location (priv->file); + + /* Keep the doc alive during the async operation. */ + g_object_ref (doc); + + g_file_query_info_async (location, + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, + G_FILE_QUERY_INFO_NONE, + G_PRIORITY_DEFAULT, + NULL, + (GAsyncReadyCallback) saved_query_info_cb, + doc); + } + ++gboolean ++gedit_document_is_untouched (GeditDocument *doc) ++{ ++ GeditDocumentPrivate *priv; ++ GFile *location; ++ ++ g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), TRUE); ++ ++ priv = gedit_document_get_instance_private (doc); ++ ++ location = gtk_source_file_get_location (priv->file); ++ ++ return location == NULL && !gtk_text_buffer_get_modified (GTK_TEXT_BUFFER (doc)); ++} ++ + gboolean + gedit_document_is_untitled (GeditDocument *doc) + { +- TeplFile *file; ++ GeditDocumentPrivate *priv; + + g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), TRUE); + +- file = tepl_buffer_get_file (TEPL_BUFFER (doc)); +- return tepl_file_get_location (file) == NULL; ++ priv = gedit_document_get_instance_private (doc); ++ ++ return gtk_source_file_get_location (priv->file) == NULL; + } + + /* + * Deletion and external modification is only checked for local files. + */ + gboolean + _gedit_document_needs_saving (GeditDocument *doc) + { + GeditDocumentPrivate *priv; + gboolean externally_modified = FALSE; + gboolean deleted = FALSE; + + g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), FALSE); + + priv = gedit_document_get_instance_private (doc); + + if (gtk_text_buffer_get_modified (GTK_TEXT_BUFFER (doc))) + { + return TRUE; + } + + if (gtk_source_file_is_local (priv->file)) + { + gtk_source_file_check_file_on_disk (priv->file); + externally_modified = gtk_source_file_is_externally_modified (priv->file); + deleted = gtk_source_file_is_deleted (priv->file); + } + + return (externally_modified || deleted) && !priv->create; + } + ++/* If @line is bigger than the lines of the document, the cursor is moved ++ * to the last line and FALSE is returned. ++ */ ++gboolean ++gedit_document_goto_line (GeditDocument *doc, ++ gint line) ++{ ++ GtkTextIter iter; ++ ++ gedit_debug (DEBUG_DOCUMENT); ++ ++ g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), FALSE); ++ g_return_val_if_fail (line >= -1, FALSE); ++ ++ gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (doc), ++ &iter, ++ line); ++ ++ gtk_text_buffer_place_cursor (GTK_TEXT_BUFFER (doc), &iter); ++ ++ return gtk_text_iter_get_line (&iter) == line; ++} ++ ++gboolean ++gedit_document_goto_line_offset (GeditDocument *doc, ++ gint line, ++ gint line_offset) ++{ ++ GtkTextIter iter; ++ ++ g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), FALSE); ++ g_return_val_if_fail (line >= -1, FALSE); ++ g_return_val_if_fail (line_offset >= -1, FALSE); ++ ++ gtk_text_buffer_get_iter_at_line_offset (GTK_TEXT_BUFFER (doc), ++ &iter, ++ line, ++ line_offset); ++ ++ gtk_text_buffer_place_cursor (GTK_TEXT_BUFFER (doc), &iter); ++ ++ return (gtk_text_iter_get_line (&iter) == line && ++ gtk_text_iter_get_line_offset (&iter) == line_offset); ++} ++ + /** + * gedit_document_set_language: + * @doc: + * @lang: (allow-none): + **/ + void + gedit_document_set_language (GeditDocument *doc, + GtkSourceLanguage *lang) + { + g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); + + set_language (doc, lang, TRUE); + } + + /** + * gedit_document_get_language: + * @doc: + * + * Return value: (transfer none): + */ + GtkSourceLanguage * + gedit_document_get_language (GeditDocument *doc) + { + g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), NULL); + + return gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (doc)); + } + + glong + _gedit_document_get_seconds_since_last_save_or_load (GeditDocument *doc) + { + GeditDocumentPrivate *priv; + GDateTime *now; + GTimeSpan n_microseconds; + + gedit_debug (DEBUG_DOCUMENT); + + g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), -1); + + priv = gedit_document_get_instance_private (doc); + + if (priv->time_of_last_save_or_load == NULL) + { + return -1; + } + + now = g_date_time_new_now_utc (); + if (now == NULL) + { + return -1; + } + + n_microseconds = g_date_time_difference (now, priv->time_of_last_save_or_load); + g_date_time_unref (now); + + return n_microseconds / (1000 * 1000); + } + ++static gchar * ++get_metadata_from_metadata_manager (GeditDocument *doc, ++ const gchar *key) ++{ ++ GeditDocumentPrivate *priv; ++ GFile *location; ++ ++ priv = gedit_document_get_instance_private (doc); ++ ++ location = gtk_source_file_get_location (priv->file); ++ ++ if (location != NULL) ++ { ++ return gedit_metadata_manager_get (priv->metadata_manager, location, key); ++ } ++ ++ return NULL; ++} ++ ++static gchar * ++get_metadata_from_gvfs (GeditDocument *doc, ++ const gchar *key) ++{ ++ GeditDocumentPrivate *priv; ++ ++ priv = gedit_document_get_instance_private (doc); ++ ++ if (priv->metadata_info != NULL && ++ g_file_info_has_attribute (priv->metadata_info, key) && ++ g_file_info_get_attribute_type (priv->metadata_info, key) == G_FILE_ATTRIBUTE_TYPE_STRING) ++ { ++ return g_strdup (g_file_info_get_attribute_string (priv->metadata_info, key)); ++ } ++ ++ return NULL; ++} ++ ++static void ++set_gvfs_metadata (GFileInfo *info, ++ const gchar *key, ++ const gchar *value) ++{ ++ g_return_if_fail (G_IS_FILE_INFO (info)); ++ ++ if (value != NULL) ++ { ++ g_file_info_set_attribute_string (info, key, value); ++ } ++ else ++ { ++ /* Unset the key */ ++ g_file_info_set_attribute (info, ++ key, ++ G_FILE_ATTRIBUTE_TYPE_INVALID, ++ NULL); ++ } ++} ++ + /** + * gedit_document_get_metadata: + * @doc: a #GeditDocument + * @key: name of the key + * + * Gets the metadata assigned to @key. + * + * Returns: the value assigned to @key. Free with g_free(). + */ + gchar * + gedit_document_get_metadata (GeditDocument *doc, + const gchar *key) + { + GeditDocumentPrivate *priv; + + g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), NULL); + g_return_val_if_fail (key != NULL, NULL); + + priv = gedit_document_get_instance_private (doc); + +- if (priv->metadata == NULL) ++ if (priv->use_gvfs_metadata) + { +- return NULL; ++ return get_metadata_from_gvfs (doc, key); + } + +- return tepl_metadata_get (priv->metadata, key); ++ return get_metadata_from_metadata_manager (doc, key); + } + + /** + * gedit_document_set_metadata: + * @doc: a #GeditDocument + * @first_key: name of the first key to set + * @...: (allow-none): value for the first key, followed optionally by more key/value pairs, + * followed by %NULL. + * + * Sets metadata on a document. + */ + void + gedit_document_set_metadata (GeditDocument *doc, + const gchar *first_key, + ...) + { + GeditDocumentPrivate *priv; +- va_list var_args; ++ GFile *location; + const gchar *key; ++ va_list var_args; ++ GFileInfo *info = NULL; + + g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); + g_return_if_fail (first_key != NULL); + + priv = gedit_document_get_instance_private (doc); + +- if (priv->metadata == NULL) ++ location = gtk_source_file_get_location (priv->file); ++ ++ /* With the metadata manager, can't set metadata for untitled documents. ++ * With GVFS metadata, if the location is NULL the metadata is stored in ++ * priv->metadata_info, so that it can be saved later if the document is ++ * saved. ++ */ ++ if (!priv->use_gvfs_metadata && location == NULL) + { + return; + } + ++ if (priv->use_gvfs_metadata) ++ { ++ info = g_file_info_new (); ++ } ++ + va_start (var_args, first_key); + +- for (key = first_key; key != NULL; key = va_arg (var_args, const gchar *)) ++ for (key = first_key; key; key = va_arg (var_args, const gchar *)) + { + const gchar *value = va_arg (var_args, const gchar *); +- tepl_metadata_set (priv->metadata, key, value); ++ ++ if (priv->use_gvfs_metadata) ++ { ++ set_gvfs_metadata (info, key, value); ++ set_gvfs_metadata (priv->metadata_info, key, value); ++ } ++ else ++ { ++ gedit_metadata_manager_set (priv->metadata_manager, location, key, value); ++ } + } + + va_end (var_args); + +- save_metadata_into_metadata_manager (doc); ++ if (priv->use_gvfs_metadata && location != NULL) ++ { ++ GError *error = NULL; ++ ++ /* We save synchronously since metadata is always local so it ++ * should be fast. Moreover this function can be called on ++ * application shutdown, when the main loop has already exited, ++ * so an async operation would not terminate. ++ * https://bugzilla.gnome.org/show_bug.cgi?id=736591 ++ */ ++ g_file_set_attributes_from_info (location, ++ info, ++ G_FILE_QUERY_INFO_NONE, ++ NULL, ++ &error); ++ ++ if (error != NULL) ++ { ++ /* Do not complain about metadata if we are closing a ++ * document for a non existing file. ++ */ ++ if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT) && ++ !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) ++ { ++ g_warning ("Set document metadata failed: %s", error->message); ++ } ++ ++ g_error_free (error); ++ } ++ } ++ ++ g_clear_object (&info); + } + + static void + update_empty_search (GeditDocument *doc) + { + GeditDocumentPrivate *priv; + gboolean new_value; + + priv = gedit_document_get_instance_private (doc); + + if (priv->search_context == NULL) + { + new_value = TRUE; + } + else + { + GtkSourceSearchSettings *search_settings; + + search_settings = gtk_source_search_context_get_settings (priv->search_context); + + new_value = gtk_source_search_settings_get_search_text (search_settings) == NULL; + } + + if (priv->empty_search != new_value) + { + priv->empty_search = new_value; + g_object_notify_by_pspec (G_OBJECT (doc), properties[PROP_EMPTY_SEARCH]); + } + } + +diff --git a/gedit/gedit-document.h b/gedit/gedit-document.h +index 901d02dc0..ed52a42fb 100644 +--- a/gedit/gedit-document.h ++++ b/gedit/gedit-document.h +@@ -1,83 +1,93 @@ + /* + * gedit-document.h + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi + * Copyright (C) 2002-2005 Paolo Maggi +- * Copyright (C) 2014-2020 Sébastien Wilmet ++ * Copyright (C) 2014 Sébastien Wilmet + * + * 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, see . + */ + + #ifndef GEDIT_DOCUMENT_H + #define GEDIT_DOCUMENT_H + +-#include ++#include + + G_BEGIN_DECLS + + #define GEDIT_TYPE_DOCUMENT (gedit_document_get_type()) + +-G_DECLARE_DERIVABLE_TYPE (GeditDocument, gedit_document, GEDIT, DOCUMENT, TeplBuffer) ++G_DECLARE_DERIVABLE_TYPE (GeditDocument, gedit_document, GEDIT, DOCUMENT, GtkSourceBuffer) + + struct _GeditDocumentClass + { +- TeplBufferClass parent_class; ++ GtkSourceBufferClass parent_class; + + /* Signals */ ++ void (* cursor_moved) (GeditDocument *document); + + void (* load) (GeditDocument *document); + + void (* loaded) (GeditDocument *document); + + void (* save) (GeditDocument *document); + + void (* saved) (GeditDocument *document); + }; + + GeditDocument *gedit_document_new (void); + + GtkSourceFile *gedit_document_get_file (GeditDocument *doc); + + gchar *gedit_document_get_short_name_for_display (GeditDocument *doc); + + gchar *gedit_document_get_content_type (GeditDocument *doc); + + gchar *gedit_document_get_mime_type (GeditDocument *doc); + ++gboolean gedit_document_is_untouched (GeditDocument *doc); ++ + gboolean gedit_document_is_untitled (GeditDocument *doc); + ++gboolean gedit_document_goto_line (GeditDocument *doc, ++ gint line); ++ ++gboolean gedit_document_goto_line_offset (GeditDocument *doc, ++ gint line, ++ gint line_offset); ++ + void gedit_document_set_language (GeditDocument *doc, + GtkSourceLanguage *lang); + GtkSourceLanguage + *gedit_document_get_language (GeditDocument *doc); + + gchar *gedit_document_get_metadata (GeditDocument *doc, + const gchar *key); + + void gedit_document_set_metadata (GeditDocument *doc, + const gchar *first_key, + ...); + + void gedit_document_set_search_context (GeditDocument *doc, + GtkSourceSearchContext *search_context); + + GtkSourceSearchContext * + gedit_document_get_search_context (GeditDocument *doc); + + G_END_DECLS + + #endif /* GEDIT_DOCUMENT_H */ + /* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-documents-panel.c b/gedit/gedit-documents-panel.c +index 98d84a98f..8033f944e 100644 +--- a/gedit/gedit-documents-panel.c ++++ b/gedit/gedit-documents-panel.c +@@ -1,56 +1,55 @@ + /* + * gedit-documents-panel.c + * This file is part of gedit + * + * Copyright (C) 2014 - Sébastien Lafargue + * + * 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, see . + */ + + #include "config.h" + + #include "gedit-documents-panel.h" + + #include +-#include + + #include "gedit-debug.h" + #include "gedit-document.h" + #include "gedit-multi-notebook.h" + #include "gedit-notebook.h" + #include "gedit-notebook-popup-menu.h" + #include "gedit-tab.h" + #include "gedit-tab-private.h" + #include "gedit-utils.h" + #include "gedit-commands-private.h" + + typedef struct _GeditDocumentsGenericRow GeditDocumentsGenericRow; + typedef struct _GeditDocumentsGenericRow GeditDocumentsGroupRow; + typedef struct _GeditDocumentsGenericRow GeditDocumentsDocumentRow; + + struct _GeditDocumentsGenericRow + { + GtkListBoxRow parent_instance; + + GeditDocumentsPanel *panel; + GtkWidget *ref; + + GtkWidget *box; + GtkWidget *label; + GtkWidget *close_button; + + /* Not used in GeditDocumentsGroupRow */ + GtkWidget *image; + GtkWidget *status_label; + }; +@@ -420,61 +419,61 @@ group_row_update_names (GeditDocumentsPanel *panel, + { + group_row_set_notebook_name (row); + } + } + + g_list_free (children); + } + + static void + group_row_refresh_visibility (GeditDocumentsPanel *panel) + { + gboolean notebook_is_unique; + GtkWidget *first_group_row; + + notebook_is_unique = gedit_multi_notebook_get_n_notebooks (panel->mnb) <= 1; + first_group_row = GTK_WIDGET (get_first_notebook_found (panel)); + + gtk_widget_set_no_show_all (first_group_row, notebook_is_unique); + gtk_widget_set_visible (first_group_row, !notebook_is_unique); + } + + static gchar * + doc_get_name (GeditDocument *doc) + { + gchar *name; + gchar *docname; + + name = gedit_document_get_short_name_for_display (doc); + + /* Truncate the name so it doesn't get insanely wide. */ +- docname = tepl_utils_str_middle_truncate (name, MAX_DOC_NAME_LENGTH); ++ docname = gedit_utils_str_middle_truncate (name, MAX_DOC_NAME_LENGTH); + + g_free (name); + + return docname; + } + + static void + document_row_sync_tab_name_and_icon (GeditTab *tab, + GParamSpec *pspec, + GtkWidget *row) + { + GeditDocumentsDocumentRow *document_row = GEDIT_DOCUMENTS_DOCUMENT_ROW (row); + GeditDocument *doc; + GtkSourceFile *file; + gchar *name; + GdkPixbuf *pixbuf; + + doc = gedit_tab_get_document (tab); + name = doc_get_name (doc); + + if (!gtk_text_buffer_get_modified (GTK_TEXT_BUFFER (doc))) + { + gtk_label_set_text (GTK_LABEL (document_row->label), name); + } + else + { + gchar *markup; + + markup = g_markup_printf_escaped ("%s", name); + gtk_label_set_markup (GTK_LABEL (document_row->label), markup); +diff --git a/gedit/gedit-factory.c b/gedit/gedit-factory.c +deleted file mode 100644 +index 409b3beb2..000000000 +--- a/gedit/gedit-factory.c ++++ /dev/null +@@ -1,50 +0,0 @@ +-/* +- * This file is part of gedit +- * +- * Copyright (C) 2020 Sébastien Wilmet +- * +- * 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, see . +- */ +- +-#include "gedit-factory.h" +-#include "gedit-dirs.h" +- +-G_DEFINE_TYPE (GeditFactory, gedit_factory, TEPL_TYPE_ABSTRACT_FACTORY) +- +-static GFile * +-gedit_factory_create_metadata_manager_file (TeplAbstractFactory *factory) +-{ +- return g_file_new_build_filename (gedit_dirs_get_user_data_dir (), +- "gedit-metadata.xml", +- NULL); +-} +- +-static void +-gedit_factory_class_init (GeditFactoryClass *klass) +-{ +- TeplAbstractFactoryClass *factory_class = TEPL_ABSTRACT_FACTORY_CLASS (klass); +- +- factory_class->create_metadata_manager_file = gedit_factory_create_metadata_manager_file; +-} +- +-static void +-gedit_factory_init (GeditFactory *factory) +-{ +-} +- +-GeditFactory * +-gedit_factory_new (void) +-{ +- return g_object_new (GEDIT_TYPE_FACTORY, NULL); +-} +diff --git a/gedit/gedit-factory.h b/gedit/gedit-factory.h +deleted file mode 100644 +index 05e19f715..000000000 +--- a/gedit/gedit-factory.h ++++ /dev/null +@@ -1,53 +0,0 @@ +-/* +- * This file is part of gedit +- * +- * Copyright (C) 2020 Sébastien Wilmet +- * +- * 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, see . +- */ +- +-#ifndef GEDIT_FACTORY_H +-#define GEDIT_FACTORY_H +- +-#include +- +-G_BEGIN_DECLS +- +-#define GEDIT_TYPE_FACTORY (gedit_factory_get_type ()) +-#define GEDIT_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_FACTORY, GeditFactory)) +-#define GEDIT_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_FACTORY, GeditFactoryClass)) +-#define GEDIT_IS_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_FACTORY)) +-#define GEDIT_IS_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_FACTORY)) +-#define GEDIT_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GEDIT_TYPE_FACTORY, GeditFactoryClass)) +- +-typedef struct _GeditFactory GeditFactory; +-typedef struct _GeditFactoryClass GeditFactoryClass; +- +-struct _GeditFactory +-{ +- TeplAbstractFactory parent; +-}; +- +-struct _GeditFactoryClass +-{ +- TeplAbstractFactoryClass parent_class; +-}; +- +-GType gedit_factory_get_type (void); +- +-GeditFactory * gedit_factory_new (void); +- +-G_END_DECLS +- +-#endif /* GEDIT_FACTORY_H */ +diff --git a/gedit/gedit-file-chooser-dialog-gtk.c b/gedit/gedit-file-chooser-dialog-gtk.c +index 4b8b4dbb0..17671ff3e 100644 +--- a/gedit/gedit-file-chooser-dialog-gtk.c ++++ b/gedit/gedit-file-chooser-dialog-gtk.c +@@ -165,131 +165,160 @@ chooser_set_current_name (GeditFileChooserDialog *dialog, + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), name); + } + + static void + chooser_set_file (GeditFileChooserDialog *dialog, + GFile *file) + { + gtk_file_chooser_set_file (GTK_FILE_CHOOSER (dialog), file, NULL); + } + + static GFile * + chooser_get_file (GeditFileChooserDialog *dialog) + { + return gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)); + } + + static void + chooser_set_do_overwrite_confirmation (GeditFileChooserDialog *dialog, + gboolean overwrite_confirmation) + { + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), overwrite_confirmation); + } + + static void + chooser_show (GeditFileChooserDialog *dialog) + { + gtk_window_present (GTK_WINDOW (dialog)); + gtk_widget_grab_focus (GTK_WIDGET (dialog)); + } + ++static void ++chooser_hide (GeditFileChooserDialog *dialog) ++{ ++ gtk_widget_hide (GTK_WIDGET (dialog)); ++} ++ + static void + chooser_destroy (GeditFileChooserDialog *dialog) + { + gtk_widget_destroy (GTK_WIDGET (dialog)); + } + + static void + chooser_set_modal (GeditFileChooserDialog *dialog, + gboolean is_modal) + { + gtk_window_set_modal (GTK_WINDOW (dialog), is_modal); + } + + static GtkWindow * + chooser_get_window (GeditFileChooserDialog *dialog) + { + return GTK_WINDOW (dialog); + } + ++static void ++chooser_add_pattern_filter (GeditFileChooserDialog *dialog, ++ const gchar *name, ++ const gchar *pattern) ++{ ++ GtkFileFilter *filter; ++ ++ filter = gtk_file_filter_new (); ++ ++ gtk_file_filter_set_name (filter, name); ++ gtk_file_filter_add_pattern (filter, pattern); ++ ++ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); ++ ++ if (gtk_file_chooser_get_filter (GTK_FILE_CHOOSER (dialog)) == NULL) ++ { ++ gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter); ++ } ++} ++ + static void + gedit_file_chooser_dialog_gtk_chooser_init (gpointer g_iface, + gpointer iface_data) + { + GeditFileChooserDialogInterface *iface = g_iface; + + iface->set_encoding = chooser_set_encoding; + iface->get_encoding = chooser_get_encoding; + + iface->set_newline_type = chooser_set_newline_type; + iface->get_newline_type = chooser_get_newline_type; + + iface->set_current_folder = chooser_set_current_folder; + iface->set_current_name = chooser_set_current_name; + iface->set_file = chooser_set_file; + iface->get_file = chooser_get_file; + iface->set_do_overwrite_confirmation = chooser_set_do_overwrite_confirmation; + iface->show = chooser_show; ++ iface->hide = chooser_hide; + iface->destroy = chooser_destroy; + iface->set_modal = chooser_set_modal; + iface->get_window = chooser_get_window; ++ iface->add_pattern_filter = chooser_add_pattern_filter; + } + + static void + gedit_file_chooser_dialog_gtk_dispose (GObject *object) + { + GeditFileChooserDialogGtk *dialog = GEDIT_FILE_CHOOSER_DIALOG_GTK (object); + + g_clear_object (&dialog->gedit_file_chooser); + + G_OBJECT_CLASS (gedit_file_chooser_dialog_gtk_parent_class)->dispose (object); + } + + static void + gedit_file_chooser_dialog_gtk_class_init (GeditFileChooserDialogGtkClass *klass) + { + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gedit_file_chooser_dialog_gtk_dispose; + } + + static void +-create_option_menu (GeditFileChooserDialogGtk *dialog) ++create_option_menu (GeditFileChooserDialogGtk *dialog, ++ GeditFileChooserFlags flags) + { + GtkWidget *label; + GtkWidget *menu; + gboolean save_mode; + + label = gtk_label_new_with_mnemonic (_("C_haracter Encoding:")); + gtk_widget_set_halign (label, GTK_ALIGN_START); + +- save_mode = TRUE; ++ save_mode = (flags & GEDIT_FILE_CHOOSER_FLAG_SAVE) != 0; + menu = gedit_encodings_combo_box_new (save_mode); + + gtk_label_set_mnemonic_widget (GTK_LABEL (label), menu); + + gtk_box_pack_start (GTK_BOX (dialog->extra_widget), + label, + FALSE, + TRUE, + 0); + + gtk_box_pack_start (GTK_BOX (dialog->extra_widget), + menu, + TRUE, + TRUE, + 0); + + gtk_widget_show (label); + gtk_widget_show (menu); + + dialog->option_menu = menu; + } + + static void + update_newline_visibility (GeditFileChooserDialogGtk *dialog) + { + gboolean visible = gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog)) == GTK_FILE_CHOOSER_ACTION_SAVE; + + gtk_widget_set_visible (dialog->newline_label, visible); + gtk_widget_set_visible (dialog->newline_combo, visible); + } +@@ -347,119 +376,138 @@ create_newline_combo (GeditFileChooserDialogGtk *dialog) + GTK_SOURCE_NEWLINE_TYPE_CR); + + newline_combo_append (GTK_COMBO_BOX (combo), + store, + &iter, + gedit_utils_newline_type_to_string (GTK_SOURCE_NEWLINE_TYPE_CR_LF), + GTK_SOURCE_NEWLINE_TYPE_CR_LF); + + gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo); + + gtk_box_pack_start (GTK_BOX (dialog->extra_widget), + label, + FALSE, + TRUE, + 0); + + gtk_box_pack_start (GTK_BOX (dialog->extra_widget), + combo, + TRUE, + TRUE, + 0); + + dialog->newline_combo = combo; + dialog->newline_label = label; + dialog->newline_store = store; + + update_newline_visibility (dialog); + } + + static void +-create_extra_widget (GeditFileChooserDialogGtk *dialog) ++create_extra_widget (GeditFileChooserDialogGtk *dialog, ++ GeditFileChooserFlags flags) + { + dialog->extra_widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_widget_show (dialog->extra_widget); + +- create_option_menu (dialog); +- create_newline_combo (dialog); ++ create_option_menu (dialog, flags); ++ ++ if ((flags & GEDIT_FILE_CHOOSER_FLAG_SAVE) != 0) ++ { ++ create_newline_combo (dialog); ++ } + + gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), dialog->extra_widget); + } + + static void + action_changed (GeditFileChooserDialogGtk *dialog, + GParamSpec *pspec, + gpointer data) + { + GtkFileChooserAction action; + + action = gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog)); + + switch (action) + { + case GTK_FILE_CHOOSER_ACTION_OPEN: + g_object_set (dialog->option_menu, + "save_mode", FALSE, + NULL); + gtk_widget_show (dialog->option_menu); + break; + case GTK_FILE_CHOOSER_ACTION_SAVE: + g_object_set (dialog->option_menu, + "save_mode", TRUE, + NULL); + gtk_widget_show (dialog->option_menu); + break; + default: + gtk_widget_hide (dialog->option_menu); + } + + update_newline_visibility (dialog); + } + + static void + gedit_file_chooser_dialog_gtk_init (GeditFileChooserDialogGtk *dialog) + { + } + + GeditFileChooserDialog * +-gedit_file_chooser_dialog_gtk_create (const gchar *title, +- GtkWindow *parent, +- const gchar *accept_label, +- const gchar *cancel_label) ++gedit_file_chooser_dialog_gtk_create (const gchar *title, ++ GtkWindow *parent, ++ GeditFileChooserFlags flags, ++ const gchar *accept_label, ++ const gchar *cancel_label) + { + GeditFileChooserDialogGtk *result; ++ GtkFileChooserAction action; ++ gboolean select_multiple; ++ ++ if ((flags & GEDIT_FILE_CHOOSER_FLAG_SAVE) != 0) ++ { ++ action = GTK_FILE_CHOOSER_ACTION_SAVE; ++ select_multiple = FALSE; ++ } ++ else ++ { ++ action = GTK_FILE_CHOOSER_ACTION_OPEN; ++ select_multiple = TRUE; ++ } + + result = g_object_new (GEDIT_TYPE_FILE_CHOOSER_DIALOG_GTK, + "title", title, + "local-only", FALSE, +- "action", GTK_FILE_CHOOSER_ACTION_SAVE, +- "select-multiple", FALSE, ++ "action", action, ++ "select-multiple", select_multiple, + NULL); + +- create_extra_widget (result); ++ create_extra_widget (result, flags); + + g_signal_connect (result, + "notify::action", + G_CALLBACK (action_changed), + NULL); + + /* FIXME: attention, there is a ref cycle here. This will be fixed when + * GeditFileChooserSave will be created (and this class removed). + */ + result->gedit_file_chooser = _gedit_file_chooser_new (); + _gedit_file_chooser_set_gtk_file_chooser (result->gedit_file_chooser, + GTK_FILE_CHOOSER (result)); + + if (parent != NULL) + { + gtk_window_set_transient_for (GTK_WINDOW (result), parent); + gtk_window_set_destroy_with_parent (GTK_WINDOW (result), TRUE); + } + + gtk_dialog_add_button (GTK_DIALOG (result), cancel_label, GTK_RESPONSE_CANCEL); + gtk_dialog_add_button (GTK_DIALOG (result), accept_label, GTK_RESPONSE_ACCEPT); + gtk_dialog_set_default_response (GTK_DIALOG (result), GTK_RESPONSE_ACCEPT); + + return GEDIT_FILE_CHOOSER_DIALOG (result); + } + + /* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-file-chooser-dialog-gtk.h b/gedit/gedit-file-chooser-dialog-gtk.h +index 0bf6a0d41..40726af54 100644 +--- a/gedit/gedit-file-chooser-dialog-gtk.h ++++ b/gedit/gedit-file-chooser-dialog-gtk.h +@@ -6,40 +6,41 @@ + * Copyright (C) 2014 - Jesse van den Kieboom + * + * 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, see . + */ + + #ifndef GEDIT_FILE_CHOOSER_DIALOG_GTK_H + #define GEDIT_FILE_CHOOSER_DIALOG_GTK_H + + #include + #include "gedit-file-chooser-dialog.h" + + G_BEGIN_DECLS + + #define GEDIT_TYPE_FILE_CHOOSER_DIALOG_GTK (gedit_file_chooser_dialog_gtk_get_type ()) + + G_DECLARE_FINAL_TYPE (GeditFileChooserDialogGtk, gedit_file_chooser_dialog_gtk, + GEDIT, FILE_CHOOSER_DIALOG_GTK, + GtkFileChooserDialog) + +-GeditFileChooserDialog * gedit_file_chooser_dialog_gtk_create (const gchar *title, +- GtkWindow *parent, +- const gchar *accept_label, +- const gchar *cancel_label); ++GeditFileChooserDialog * gedit_file_chooser_dialog_gtk_create (const gchar *title, ++ GtkWindow *parent, ++ GeditFileChooserFlags flags, ++ const gchar *accept_label, ++ const gchar *cancel_label); + + G_END_DECLS + + #endif /* GEDIT_FILE_CHOOSER_DIALOG_GTK_H */ + + /* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-file-chooser-dialog.c b/gedit/gedit-file-chooser-dialog.c +index 56edbae3b..b4ce2fd9e 100644 +--- a/gedit/gedit-file-chooser-dialog.c ++++ b/gedit/gedit-file-chooser-dialog.c +@@ -35,67 +35,69 @@ confirm_overwrite_accumulator (GSignalInvocationHint *ihint, + + conf = g_value_get_enum (handler_return); + g_value_set_enum (return_accu, conf); + continue_emission = (conf == GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM); + + return continue_emission; + } + + static void + gedit_file_chooser_dialog_default_init (GeditFileChooserDialogInterface *iface) + { + g_signal_new ("response", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, + 1, + G_TYPE_INT); + + g_signal_new ("confirm-overwrite", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + 0, + confirm_overwrite_accumulator, NULL, NULL, + GTK_TYPE_FILE_CHOOSER_CONFIRMATION, + 0); + } + + GeditFileChooserDialog * +-gedit_file_chooser_dialog_create (const gchar *title, +- GtkWindow *parent, +- const gchar *accept_label, +- const gchar *cancel_label) ++gedit_file_chooser_dialog_create (const gchar *title, ++ GtkWindow *parent, ++ GeditFileChooserFlags flags, ++ const gchar *accept_label, ++ const gchar *cancel_label) + { + return gedit_file_chooser_dialog_gtk_create (title, + parent, ++ flags, + accept_label, + cancel_label); + } + + void + gedit_file_chooser_dialog_set_encoding (GeditFileChooserDialog *dialog, + const GtkSourceEncoding *encoding) + { + GeditFileChooserDialogInterface *iface; + + g_return_if_fail (GEDIT_IS_FILE_CHOOSER_DIALOG (dialog)); + + iface = GEDIT_FILE_CHOOSER_DIALOG_GET_IFACE (dialog); + g_return_if_fail (iface->set_encoding != NULL); + + iface->set_encoding (dialog, encoding); + } + + const GtkSourceEncoding * + gedit_file_chooser_dialog_get_encoding (GeditFileChooserDialog *dialog) + { + GeditFileChooserDialogInterface *iface; + + g_return_val_if_fail (GEDIT_IS_FILE_CHOOSER_DIALOG (dialog), NULL); + + iface = GEDIT_FILE_CHOOSER_DIALOG_GET_IFACE (dialog); + g_return_val_if_fail (iface->get_encoding != NULL, NULL); + + return iface->get_encoding (dialog); + } +@@ -184,75 +186,105 @@ gedit_file_chooser_dialog_get_file (GeditFileChooserDialog *dialog) + return iface->get_file (dialog); + } + + void + gedit_file_chooser_dialog_set_do_overwrite_confirmation (GeditFileChooserDialog *dialog, + gboolean overwrite_confirmation) + { + GeditFileChooserDialogInterface *iface; + + g_return_if_fail (GEDIT_IS_FILE_CHOOSER_DIALOG (dialog)); + + iface = GEDIT_FILE_CHOOSER_DIALOG_GET_IFACE (dialog); + g_return_if_fail (iface->set_do_overwrite_confirmation != NULL); + + iface->set_do_overwrite_confirmation (dialog, overwrite_confirmation); + } + + void + gedit_file_chooser_dialog_show (GeditFileChooserDialog *dialog) + { + GeditFileChooserDialogInterface *iface; + + g_return_if_fail (GEDIT_IS_FILE_CHOOSER_DIALOG (dialog)); + + iface = GEDIT_FILE_CHOOSER_DIALOG_GET_IFACE (dialog); + g_return_if_fail (iface->show != NULL); + + iface->show (dialog); + } + ++void ++gedit_file_chooser_dialog_hide (GeditFileChooserDialog *dialog) ++{ ++ GeditFileChooserDialogInterface *iface; ++ ++ g_return_if_fail (GEDIT_IS_FILE_CHOOSER_DIALOG (dialog)); ++ ++ iface = GEDIT_FILE_CHOOSER_DIALOG_GET_IFACE (dialog); ++ g_return_if_fail (iface->hide != NULL); ++ ++ iface->hide (dialog); ++} ++ + void + gedit_file_chooser_dialog_destroy (GeditFileChooserDialog *dialog) + { + GeditFileChooserDialogInterface *iface; + + g_return_if_fail (GEDIT_IS_FILE_CHOOSER_DIALOG (dialog)); + + iface = GEDIT_FILE_CHOOSER_DIALOG_GET_IFACE (dialog); + g_return_if_fail (iface->destroy != NULL); + + iface->destroy (dialog); + } + + void + gedit_file_chooser_dialog_set_modal (GeditFileChooserDialog *dialog, + gboolean is_modal) + { + GeditFileChooserDialogInterface *iface; + + g_return_if_fail (GEDIT_IS_FILE_CHOOSER_DIALOG (dialog)); + + iface = GEDIT_FILE_CHOOSER_DIALOG_GET_IFACE (dialog); + g_return_if_fail (iface->set_modal != NULL); + + iface->set_modal (dialog, is_modal); + } + + GtkWindow * + gedit_file_chooser_dialog_get_window (GeditFileChooserDialog *dialog) + { + GeditFileChooserDialogInterface *iface; + + g_return_val_if_fail (GEDIT_IS_FILE_CHOOSER_DIALOG (dialog), NULL); + + iface = GEDIT_FILE_CHOOSER_DIALOG_GET_IFACE (dialog); + + if (iface->get_window) + { + return iface->get_window (dialog); + } + + return NULL; + } + ++void ++gedit_file_chooser_dialog_add_pattern_filter (GeditFileChooserDialog *dialog, ++ const gchar *name, ++ const gchar *pattern) ++{ ++ GeditFileChooserDialogInterface *iface; ++ ++ g_return_if_fail (GEDIT_IS_FILE_CHOOSER_DIALOG (dialog)); ++ ++ iface = GEDIT_FILE_CHOOSER_DIALOG_GET_IFACE (dialog); ++ ++ if (iface->add_pattern_filter) ++ { ++ iface->add_pattern_filter (dialog, name, pattern); ++ } ++} ++ + /* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-file-chooser-dialog.h b/gedit/gedit-file-chooser-dialog.h +index b759c6566..633950e05 100644 +--- a/gedit/gedit-file-chooser-dialog.h ++++ b/gedit/gedit-file-chooser-dialog.h +@@ -37,87 +37,104 @@ struct _GeditFileChooserDialogInterface + + /* Virtual public methods */ + void (*set_encoding) (GeditFileChooserDialog *dialog, + const GtkSourceEncoding *encoding); + + const GtkSourceEncoding * + (*get_encoding) (GeditFileChooserDialog *dialog); + + void (*set_newline_type) (GeditFileChooserDialog *dialog, + GtkSourceNewlineType newline_type); + + GtkSourceNewlineType + (*get_newline_type) (GeditFileChooserDialog *dialog); + + void (*set_current_folder) (GeditFileChooserDialog *dialog, + GFile *folder); + + void (*set_current_name) (GeditFileChooserDialog *dialog, + const gchar *name); + + void (*set_file) (GeditFileChooserDialog *dialog, + GFile *file); + + GFile * (*get_file) (GeditFileChooserDialog *dialog); + + void (*set_do_overwrite_confirmation) + (GeditFileChooserDialog *dialog, + gboolean overwrite_confirmation); + + void (*show) (GeditFileChooserDialog *dialog); ++ void (*hide) (GeditFileChooserDialog *dialog); + + void (*destroy) (GeditFileChooserDialog *dialog); + + void (*set_modal) (GeditFileChooserDialog *dialog, + gboolean is_modal); + + GtkWindow * + (*get_window) (GeditFileChooserDialog *dialog); ++ ++ void (*add_pattern_filter) (GeditFileChooserDialog *dilaog, ++ const gchar *name, ++ const gchar *pattern); + }; + ++typedef enum ++{ ++ GEDIT_FILE_CHOOSER_FLAG_SAVE = 1 << 0, ++ GEDIT_FILE_CHOOSER_FLAG_OPEN = 1 << 1 ++} GeditFileChooserFlags; ++ + GeditFileChooserDialog * + gedit_file_chooser_dialog_create (const gchar *title, + GtkWindow *parent, ++ GeditFileChooserFlags flags, + const gchar *accept_label, + const gchar *cancel_label); + + void gedit_file_chooser_dialog_destroy (GeditFileChooserDialog *dialog); + + void gedit_file_chooser_dialog_set_encoding (GeditFileChooserDialog *dialog, + const GtkSourceEncoding *encoding); + + const GtkSourceEncoding * + gedit_file_chooser_dialog_get_encoding (GeditFileChooserDialog *dialog); + + void gedit_file_chooser_dialog_set_newline_type (GeditFileChooserDialog *dialog, + GtkSourceNewlineType newline_type); + + GtkSourceNewlineType + gedit_file_chooser_dialog_get_newline_type (GeditFileChooserDialog *dialog); + + void gedit_file_chooser_dialog_set_current_folder (GeditFileChooserDialog *dialog, + GFile *folder); + + void gedit_file_chooser_dialog_set_current_name (GeditFileChooserDialog *dialog, + const gchar *name); + + void gedit_file_chooser_dialog_set_file (GeditFileChooserDialog *dialog, + GFile *file); + + GFile *gedit_file_chooser_dialog_get_file (GeditFileChooserDialog *dialog); + + void gedit_file_chooser_dialog_set_do_overwrite_confirmation ( + GeditFileChooserDialog *dialog, + gboolean overwrite_confirmation); + + void gedit_file_chooser_dialog_show (GeditFileChooserDialog *dialog); ++void gedit_file_chooser_dialog_hide (GeditFileChooserDialog *dialog); + + void gedit_file_chooser_dialog_set_modal (GeditFileChooserDialog *dialog, + gboolean is_modal); + + GtkWindow *gedit_file_chooser_dialog_get_window (GeditFileChooserDialog *dialog); + ++void gedit_file_chooser_dialog_add_pattern_filter (GeditFileChooserDialog *dialog, ++ const gchar *name, ++ const gchar *pattern); ++ + G_END_DECLS + + #endif /* GEDIT_FILE_CHOOSER_DIALOG_H */ + + /* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-highlight-mode-dialog.c b/gedit/gedit-highlight-mode-dialog.c +new file mode 100644 +index 000000000..a0661bfc3 +--- /dev/null ++++ b/gedit/gedit-highlight-mode-dialog.c +@@ -0,0 +1,102 @@ ++/* ++ * gedit-highlight-mode-dialog.c ++ * This file is part of gedit ++ * ++ * Copyright (C) 2013 - Ignacio Casal Quinteiro ++ * ++ * gedit 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. ++ * ++ * gedit 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 gedit. If not, see . ++ */ ++ ++#include "gedit-highlight-mode-dialog.h" ++ ++struct _GeditHighlightModeDialog ++{ ++ GtkDialog parent_instance; ++ ++ GeditHighlightModeSelector *selector; ++ gulong on_language_selected_id; ++}; ++ ++G_DEFINE_TYPE (GeditHighlightModeDialog, gedit_highlight_mode_dialog, GTK_TYPE_DIALOG) ++ ++static void ++gedit_highlight_mode_dialog_response (GtkDialog *dialog, ++ gint response_id) ++{ ++ GeditHighlightModeDialog *dlg = GEDIT_HIGHLIGHT_MODE_DIALOG (dialog); ++ ++ if (response_id == GTK_RESPONSE_OK) ++ { ++ g_signal_handler_block (dlg->selector, dlg->on_language_selected_id); ++ gedit_highlight_mode_selector_activate_selected_language (dlg->selector); ++ g_signal_handler_unblock (dlg->selector, dlg->on_language_selected_id); ++ } ++ ++ gtk_widget_destroy (GTK_WIDGET (dialog)); ++} ++ ++static void ++on_language_selected (GeditHighlightModeSelector *sel, ++ GtkSourceLanguage *language, ++ GeditHighlightModeDialog *dlg) ++{ ++ g_signal_handler_block (dlg->selector, dlg->on_language_selected_id); ++ gedit_highlight_mode_selector_activate_selected_language (dlg->selector); ++ g_signal_handler_unblock (dlg->selector, dlg->on_language_selected_id); ++ ++ gtk_widget_destroy (GTK_WIDGET (dlg)); ++} ++ ++static void ++gedit_highlight_mode_dialog_class_init (GeditHighlightModeDialogClass *klass) ++{ ++ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); ++ GtkDialogClass *dialog_class = GTK_DIALOG_CLASS (klass); ++ ++ dialog_class->response = gedit_highlight_mode_dialog_response; ++ ++ /* Bind class to template */ ++ gtk_widget_class_set_template_from_resource (widget_class, ++ "/org/gnome/gedit/ui/gedit-highlight-mode-dialog.ui"); ++ gtk_widget_class_bind_template_child (widget_class, GeditHighlightModeDialog, selector); ++} ++ ++static void ++gedit_highlight_mode_dialog_init (GeditHighlightModeDialog *dlg) ++{ ++ gtk_widget_init_template (GTK_WIDGET (dlg)); ++ gtk_dialog_set_default_response (GTK_DIALOG (dlg), GTK_RESPONSE_OK); ++ ++ dlg->on_language_selected_id = g_signal_connect (dlg->selector, "language-selected", ++ G_CALLBACK (on_language_selected), dlg); ++} ++ ++GtkWidget * ++gedit_highlight_mode_dialog_new (GtkWindow *parent) ++{ ++ return GTK_WIDGET (g_object_new (GEDIT_TYPE_HIGHLIGHT_MODE_DIALOG, ++ "transient-for", parent, ++ "use-header-bar", TRUE, ++ NULL)); ++} ++ ++GeditHighlightModeSelector * ++gedit_highlight_mode_dialog_get_selector (GeditHighlightModeDialog *dlg) ++{ ++ g_return_val_if_fail (GEDIT_IS_HIGHLIGHT_MODE_DIALOG (dlg), NULL); ++ ++ return dlg->selector; ++} ++ ++/* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-highlight-mode-dialog.h b/gedit/gedit-highlight-mode-dialog.h +new file mode 100644 +index 000000000..57d406bb3 +--- /dev/null ++++ b/gedit/gedit-highlight-mode-dialog.h +@@ -0,0 +1,41 @@ ++/* ++ * gedit-highlight-mode-dialog.h ++ * This file is part of gedit ++ * ++ * Copyright (C) 2013 - Ignacio Casal Quinteiro ++ * ++ * gedit 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. ++ * ++ * gedit 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 gedit. If not, see . ++ */ ++ ++ ++#ifndef GEDIT_HIGHLIGHT_MODE_DIALOG_H ++#define GEDIT_HIGHLIGHT_MODE_DIALOG_H ++ ++#include "gedit-highlight-mode-selector.h" ++ ++G_BEGIN_DECLS ++ ++#define GEDIT_TYPE_HIGHLIGHT_MODE_DIALOG (gedit_highlight_mode_dialog_get_type ()) ++ ++G_DECLARE_FINAL_TYPE (GeditHighlightModeDialog, gedit_highlight_mode_dialog, GEDIT, HIGHLIGHT_MODE_DIALOG, GtkDialog) ++ ++GtkWidget *gedit_highlight_mode_dialog_new (GtkWindow *parent); ++ ++GeditHighlightModeSelector *gedit_highlight_mode_dialog_get_selector (GeditHighlightModeDialog *dlg); ++ ++G_END_DECLS ++ ++#endif /* GEDIT_HIGHLIGHT_MODE_DIALOG_H */ ++ ++/* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-highlight-mode-selector.c b/gedit/gedit-highlight-mode-selector.c +new file mode 100644 +index 000000000..cb6bd466f +--- /dev/null ++++ b/gedit/gedit-highlight-mode-selector.c +@@ -0,0 +1,375 @@ ++/* ++ * gedit-highlight-mode-selector.c ++ * This file is part of gedit ++ * ++ * Copyright (C) 2013 - Ignacio Casal Quinteiro ++ * ++ * gedit 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. ++ * ++ * gedit 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 gedit. If not, see . ++ */ ++ ++#include "gedit-highlight-mode-selector.h" ++#include ++ ++enum ++{ ++ COLUMN_NAME, ++ COLUMN_LANG, ++ N_COLUMNS ++}; ++ ++struct _GeditHighlightModeSelector ++{ ++ GtkGrid parent_instance; ++ ++ GtkWidget *treeview; ++ GtkWidget *entry; ++ GtkListStore *liststore; ++ GtkTreeModelFilter *treemodelfilter; ++ GtkTreeSelection *treeview_selection; ++}; ++ ++/* Signals */ ++enum ++{ ++ LANGUAGE_SELECTED, ++ LAST_SIGNAL ++}; ++ ++static guint signals[LAST_SIGNAL] = { 0 }; ++ ++G_DEFINE_TYPE (GeditHighlightModeSelector, gedit_highlight_mode_selector, GTK_TYPE_GRID) ++ ++static void ++gedit_highlight_mode_selector_language_selected (GeditHighlightModeSelector *widget, ++ GtkSourceLanguage *language) ++{ ++} ++ ++static void ++gedit_highlight_mode_selector_class_init (GeditHighlightModeSelectorClass *klass) ++{ ++ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); ++ ++ signals[LANGUAGE_SELECTED] = ++ g_signal_new_class_handler ("language-selected", ++ G_TYPE_FROM_CLASS (klass), ++ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, ++ G_CALLBACK (gedit_highlight_mode_selector_language_selected), ++ NULL, NULL, NULL, ++ G_TYPE_NONE, ++ 1, ++ GTK_SOURCE_TYPE_LANGUAGE); ++ ++ /* Bind class to template */ ++ gtk_widget_class_set_template_from_resource (widget_class, ++ "/org/gnome/gedit/ui/gedit-highlight-mode-selector.ui"); ++ gtk_widget_class_bind_template_child (widget_class, GeditHighlightModeSelector, treeview); ++ gtk_widget_class_bind_template_child (widget_class, GeditHighlightModeSelector, entry); ++ gtk_widget_class_bind_template_child (widget_class, GeditHighlightModeSelector, liststore); ++ gtk_widget_class_bind_template_child (widget_class, GeditHighlightModeSelector, treemodelfilter); ++ gtk_widget_class_bind_template_child (widget_class, GeditHighlightModeSelector, treeview_selection); ++} ++ ++static gboolean ++visible_func (GtkTreeModel *model, ++ GtkTreeIter *iter, ++ GeditHighlightModeSelector *selector) ++{ ++ const gchar *entry_text; ++ gchar *name; ++ gchar *name_normalized; ++ gchar *name_casefolded; ++ gchar *text_normalized; ++ gchar *text_casefolded; ++ gboolean visible = FALSE; ++ ++ entry_text = gtk_entry_get_text (GTK_ENTRY (selector->entry)); ++ ++ if (*entry_text == '\0') ++ { ++ return TRUE; ++ } ++ ++ gtk_tree_model_get (model, iter, COLUMN_NAME, &name, -1); ++ ++ name_normalized = g_utf8_normalize (name, -1, G_NORMALIZE_ALL); ++ g_free (name); ++ ++ name_casefolded = g_utf8_casefold (name_normalized, -1); ++ g_free (name_normalized); ++ ++ text_normalized = g_utf8_normalize (entry_text, -1, G_NORMALIZE_ALL); ++ text_casefolded = g_utf8_casefold (text_normalized, -1); ++ g_free (text_normalized); ++ ++ if (strstr (name_casefolded, text_casefolded) != NULL) ++ { ++ visible = TRUE; ++ } ++ ++ g_free (name_casefolded); ++ g_free (text_casefolded); ++ ++ return visible; ++} ++ ++static void ++on_entry_activate (GtkEntry *entry, ++ GeditHighlightModeSelector *selector) ++{ ++ gedit_highlight_mode_selector_activate_selected_language (selector); ++} ++ ++static void ++on_entry_changed (GtkEntry *entry, ++ GeditHighlightModeSelector *selector) ++{ ++ GtkTreeIter iter; ++ ++ gtk_tree_model_filter_refilter (selector->treemodelfilter); ++ ++ if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (selector->treemodelfilter), &iter)) ++ { ++ gtk_tree_selection_select_iter (selector->treeview_selection, &iter); ++ } ++} ++ ++static gboolean ++move_selection (GeditHighlightModeSelector *selector, ++ gint howmany) ++{ ++ GtkTreeIter iter; ++ GtkTreePath *path; ++ gint *indices; ++ gint ret = FALSE; ++ ++ if (!gtk_tree_selection_get_selected (selector->treeview_selection, NULL, &iter) && ++ !gtk_tree_model_get_iter_first (GTK_TREE_MODEL (selector->treemodelfilter), &iter)) ++ { ++ return FALSE; ++ } ++ ++ path = gtk_tree_model_get_path (GTK_TREE_MODEL (selector->treemodelfilter), &iter); ++ indices = gtk_tree_path_get_indices (path); ++ ++ if (indices) ++ { ++ gint num; ++ gint idx; ++ GtkTreePath *new_path; ++ ++ idx = indices[0]; ++ num = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (selector->treemodelfilter), NULL); ++ ++ if ((idx + howmany) < 0) ++ { ++ idx = 0; ++ } ++ else if ((idx + howmany) >= num) ++ { ++ idx = num - 1; ++ } ++ else ++ { ++ idx = idx + howmany; ++ } ++ ++ new_path = gtk_tree_path_new_from_indices (idx, -1); ++ gtk_tree_selection_select_path (selector->treeview_selection, new_path); ++ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (selector->treeview), ++ new_path, NULL, TRUE, 0.5, 0); ++ gtk_tree_path_free (new_path); ++ ++ ret = TRUE; ++ } ++ ++ gtk_tree_path_free (path); ++ ++ return ret; ++} ++ ++static gboolean ++on_entry_key_press_event (GtkWidget *entry, ++ GdkEventKey *event, ++ GeditHighlightModeSelector *selector) ++{ ++ if (event->keyval == GDK_KEY_Down) ++ { ++ return move_selection (selector, 1); ++ } ++ else if (event->keyval == GDK_KEY_Up) ++ { ++ return move_selection (selector, -1); ++ } ++ else if (event->keyval == GDK_KEY_Page_Down) ++ { ++ return move_selection (selector, 5); ++ } ++ else if (event->keyval == GDK_KEY_Page_Up) ++ { ++ return move_selection (selector, -5); ++ } ++ ++ return FALSE; ++} ++ ++static void ++on_row_activated (GtkTreeView *tree_view, ++ GtkTreePath *path, ++ GtkTreeViewColumn *column, ++ GeditHighlightModeSelector *selector) ++{ ++ gedit_highlight_mode_selector_activate_selected_language (selector); ++} ++ ++static void ++gedit_highlight_mode_selector_init (GeditHighlightModeSelector *selector) ++{ ++ GtkSourceLanguageManager *lm; ++ const gchar * const *ids; ++ gint i; ++ GtkTreeIter iter; ++ ++ selector = gedit_highlight_mode_selector_get_instance_private (selector); ++ ++ gtk_widget_init_template (GTK_WIDGET (selector)); ++ ++ gtk_tree_model_filter_set_visible_func (selector->treemodelfilter, ++ (GtkTreeModelFilterVisibleFunc)visible_func, ++ selector, ++ NULL); ++ ++ g_signal_connect (selector->entry, "activate", ++ G_CALLBACK (on_entry_activate), selector); ++ g_signal_connect (selector->entry, "changed", ++ G_CALLBACK (on_entry_changed), selector); ++ g_signal_connect (selector->entry, "key-press-event", ++ G_CALLBACK (on_entry_key_press_event), selector); ++ ++ g_signal_connect (selector->treeview, "row-activated", ++ G_CALLBACK (on_row_activated), selector); ++ ++ /* Populate tree model */ ++ gtk_list_store_append (selector->liststore, &iter); ++ gtk_list_store_set (selector->liststore, &iter, ++ COLUMN_NAME, _("Plain Text"), ++ COLUMN_LANG, NULL, ++ -1); ++ ++ lm = gtk_source_language_manager_get_default (); ++ ids = gtk_source_language_manager_get_language_ids (lm); ++ ++ for (i = 0; ids[i] != NULL; i++) ++ { ++ GtkSourceLanguage *lang; ++ ++ lang = gtk_source_language_manager_get_language (lm, ids[i]); ++ ++ if (!gtk_source_language_get_hidden (lang)) ++ { ++ gtk_list_store_append (selector->liststore, &iter); ++ gtk_list_store_set (selector->liststore, &iter, ++ COLUMN_NAME, gtk_source_language_get_name (lang), ++ COLUMN_LANG, lang, ++ -1); ++ } ++ } ++ ++ /* select first item */ ++ if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (selector->treemodelfilter), &iter)) ++ { ++ gtk_tree_selection_select_iter (selector->treeview_selection, &iter); ++ } ++} ++ ++GeditHighlightModeSelector * ++gedit_highlight_mode_selector_new () ++{ ++ return g_object_new (GEDIT_TYPE_HIGHLIGHT_MODE_SELECTOR, NULL); ++} ++ ++void ++gedit_highlight_mode_selector_select_language (GeditHighlightModeSelector *selector, ++ GtkSourceLanguage *language) ++{ ++ GtkTreeIter iter; ++ ++ g_return_if_fail (GEDIT_IS_HIGHLIGHT_MODE_SELECTOR (selector)); ++ ++ if (language == NULL) ++ { ++ return; ++ } ++ ++ if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (selector->treemodelfilter), &iter)) ++ { ++ do ++ { ++ GtkSourceLanguage *lang; ++ ++ gtk_tree_model_get (GTK_TREE_MODEL (selector->treemodelfilter), ++ &iter, ++ COLUMN_LANG, &lang, ++ -1); ++ ++ if (lang != NULL) ++ { ++ gboolean equal = (lang == language); ++ ++ g_object_unref (lang); ++ ++ if (equal) ++ { ++ GtkTreePath *path; ++ ++ path = gtk_tree_model_get_path (GTK_TREE_MODEL (selector->treemodelfilter), &iter); ++ ++ gtk_tree_selection_select_iter (selector->treeview_selection, &iter); ++ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (selector->treeview), ++ path, NULL, TRUE, 0.5, 0); ++ gtk_tree_path_free (path); ++ break; ++ } ++ } ++ } ++ while (gtk_tree_model_iter_next (GTK_TREE_MODEL (selector->treemodelfilter), &iter)); ++ } ++} ++ ++void ++gedit_highlight_mode_selector_activate_selected_language (GeditHighlightModeSelector *selector) ++{ ++ GtkSourceLanguage *lang; ++ GtkTreeIter iter; ++ ++ g_return_if_fail (GEDIT_IS_HIGHLIGHT_MODE_SELECTOR (selector)); ++ ++ if (!gtk_tree_selection_get_selected (selector->treeview_selection, NULL, &iter)) ++ { ++ return; ++ } ++ ++ gtk_tree_model_get (GTK_TREE_MODEL (selector->treemodelfilter), &iter, ++ COLUMN_LANG, &lang, ++ -1); ++ ++ g_signal_emit (G_OBJECT (selector), signals[LANGUAGE_SELECTED], 0, lang); ++ ++ if (lang != NULL) ++ { ++ g_object_unref (lang); ++ } ++} ++ ++/* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-highlight-mode-selector.h b/gedit/gedit-highlight-mode-selector.h +new file mode 100644 +index 000000000..dc19e3cc4 +--- /dev/null ++++ b/gedit/gedit-highlight-mode-selector.h +@@ -0,0 +1,44 @@ ++/* ++ * gedit-highlight-mode-selector.h ++ * This file is part of gedit ++ * ++ * Copyright (C) 2013 - Ignacio Casal Quinteiro ++ * ++ * gedit 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. ++ * ++ * gedit 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 gedit. If not, see . ++ */ ++ ++#ifndef GEDIT_HIGHLIGHT_MODE_SELECTOR_H ++#define GEDIT_HIGHLIGHT_MODE_SELECTOR_H ++ ++#include ++ ++G_BEGIN_DECLS ++ ++#define GEDIT_TYPE_HIGHLIGHT_MODE_SELECTOR (gedit_highlight_mode_selector_get_type ()) ++ ++G_DECLARE_FINAL_TYPE (GeditHighlightModeSelector, gedit_highlight_mode_selector, GEDIT, HIGHLIGHT_MODE_SELECTOR, GtkGrid) ++ ++GeditHighlightModeSelector *gedit_highlight_mode_selector_new (void); ++ ++void gedit_highlight_mode_selector_select_language (GeditHighlightModeSelector *selector, ++ GtkSourceLanguage *language); ++ ++void gedit_highlight_mode_selector_activate_selected_language ++ (GeditHighlightModeSelector *selector); ++ ++G_END_DECLS ++ ++#endif /* GEDIT_HIGHLIGHT_MODE_SELECTOR_H */ ++ ++/* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-io-error-info-bar.c b/gedit/gedit-io-error-info-bar.c +index 1edbe313a..1539e3b7b 100644 +--- a/gedit/gedit-io-error-info-bar.c ++++ b/gedit/gedit-io-error-info-bar.c +@@ -1,58 +1,65 @@ + /* + * gedit-io-error-info-bar.c + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi + * + * 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, see . + */ + + /* + * Verbose error reporting for file I/O operations (load, save, revert, create) + */ + + #include "gedit-io-error-info-bar.h" ++ ++#include ++#include + #include +-#include ++#include ++ + #include "gedit-encodings-combo-box.h" ++#include "gedit-settings.h" ++#include "gedit-utils.h" ++#include "gedit-document.h" + + #define MAX_URI_IN_DIALOG_LENGTH 50 + + static gboolean + is_recoverable_error (const GError *error) + { + gboolean is_recoverable = FALSE; + + if (error->domain == G_IO_ERROR) + { + switch (error->code) + { + case G_IO_ERROR_PERMISSION_DENIED: + case G_IO_ERROR_NOT_FOUND: + case G_IO_ERROR_HOST_NOT_FOUND: + case G_IO_ERROR_TIMED_OUT: + case G_IO_ERROR_NOT_MOUNTABLE_FILE: + case G_IO_ERROR_NOT_MOUNTED: + case G_IO_ERROR_BUSY: + is_recoverable = TRUE; + break; + default: + break; + } + } + + return is_recoverable; + } + + static gboolean +@@ -193,61 +200,61 @@ parse_gio_error (gint code, + + case G_IO_ERROR_IS_DIRECTORY: + *error_message = g_strdup_printf (_("“%s” is a directory."), + uri_for_display); + *message_details = g_strdup (_("Please check that you typed the " + "location correctly and try again.")); + break; + + case G_IO_ERROR_INVALID_FILENAME: + *error_message = g_strdup_printf (_("“%s” is not a valid location."), + uri_for_display); + *message_details = g_strdup (_("Please check that you typed the " + "location correctly and try again.")); + break; + + case G_IO_ERROR_HOST_NOT_FOUND: + /* This case can be hit for user-typed strings like "foo" due to + * the code that guesses web addresses when there's no initial "/". + * But this case is also hit for legitimate web addresses when + * the proxy is set up wrong. + */ + { + gchar *hn = NULL; + gchar *uri = NULL; + + if (location) + { + uri = g_file_get_uri (location); + } + +- if (uri && tepl_utils_decode_uri (uri, NULL, NULL, &hn, NULL, NULL)) ++ if (uri && gedit_utils_decode_uri (uri, NULL, NULL, &hn, NULL, NULL)) + { + if (hn != NULL) + { + gchar *host_markup; + gchar *host_name; + + host_name = g_utf8_make_valid (hn, -1); + g_free (hn); + + host_markup = g_markup_escape_text (host_name, -1); + g_free (host_name); + + *message_details = g_strdup_printf ( + /* Translators: %s is a host name */ + _("Host “%s” could not be found. " + "Please check that your proxy settings " + "are correct and try again."), + host_markup); + + g_free (host_markup); + } + } + + g_free (uri); + + if (!*message_details) + { + /* use the same string as INVALID_HOST */ + *message_details = g_strdup_printf ( + _("Hostname was invalid. " +@@ -296,62 +303,62 @@ parse_error (const GError *error, + { + g_warning ("Hit unhandled case %d (%s) in %s.", + error->code, error->message, G_STRFUNC); + *message_details = g_strdup_printf (_("Unexpected error: %s"), + error->message); + } + } + + GtkWidget * + gedit_unrecoverable_reverting_error_info_bar_new (GFile *location, + const GError *error) + { + gchar *error_message = NULL; + gchar *message_details = NULL; + gchar *full_formatted_uri; + gchar *uri_for_display; + gchar *temp_uri_for_display; + GtkWidget *info_bar; + + g_return_val_if_fail (G_IS_FILE (location), NULL); + g_return_val_if_fail (error != NULL, NULL); + g_return_val_if_fail (error->domain == GTK_SOURCE_FILE_LOADER_ERROR || + error->domain == G_IO_ERROR, NULL); + + full_formatted_uri = g_file_get_parse_name (location); + + /* Truncate the URI so it doesn't get insanely wide. Note that even + * though the dialog uses wrapped text, if the URI doesn't contain + * white space then the text-wrapping code is too stupid to wrap it. + */ +- temp_uri_for_display = tepl_utils_str_middle_truncate (full_formatted_uri, +- MAX_URI_IN_DIALOG_LENGTH); ++ temp_uri_for_display = gedit_utils_str_middle_truncate (full_formatted_uri, ++ MAX_URI_IN_DIALOG_LENGTH); + g_free (full_formatted_uri); + + uri_for_display = g_markup_escape_text (temp_uri_for_display, -1); + g_free (temp_uri_for_display); + + if (is_gio_error (error, G_IO_ERROR_NOT_FOUND)) + { + message_details = g_strdup (_("Cannot find the requested file. " + "Perhaps it has recently been deleted.")); + } + else + { + parse_error (error, &error_message, &message_details, location, uri_for_display); + } + + if (error_message == NULL) + { + error_message = g_strdup_printf (_("Could not revert the file “%s”."), + uri_for_display); + } + + info_bar = create_io_loading_error_info_bar (error_message, + message_details, + FALSE); + + g_free (uri_for_display); + g_free (error_message); + g_free (message_details); + + return info_bar; +@@ -472,62 +479,62 @@ gedit_io_loading_error_info_bar_new (GFile *location, + const GtkSourceEncoding *encoding, + const GError *error) + { + gchar *error_message = NULL; + gchar *message_details = NULL; + gchar *full_formatted_uri; + gchar *uri_for_display; + gchar *temp_uri_for_display; + GtkWidget *info_bar; + gboolean edit_anyway = FALSE; + gboolean convert_error = FALSE; + + g_return_val_if_fail (error != NULL, NULL); + g_return_val_if_fail (error->domain == GTK_SOURCE_FILE_LOADER_ERROR || + error->domain == G_IO_ERROR || + error->domain == G_CONVERT_ERROR, NULL); + + if (location != NULL) + { + full_formatted_uri = g_file_get_parse_name (location); + } + else + { + full_formatted_uri = g_strdup ("stdin"); + } + + /* Truncate the URI so it doesn't get insanely wide. Note that even + * though the dialog uses wrapped text, if the URI doesn't contain + * white space then the text-wrapping code is too stupid to wrap it. + */ +- temp_uri_for_display = tepl_utils_str_middle_truncate (full_formatted_uri, +- MAX_URI_IN_DIALOG_LENGTH); ++ temp_uri_for_display = gedit_utils_str_middle_truncate (full_formatted_uri, ++ MAX_URI_IN_DIALOG_LENGTH); + g_free (full_formatted_uri); + + uri_for_display = g_markup_escape_text (temp_uri_for_display, -1); + g_free (temp_uri_for_display); + + if (is_gio_error (error, G_IO_ERROR_TOO_MANY_LINKS)) + { + message_details = g_strdup (_("The number of followed links is limited and the actual file could not be found within this limit.")); + } + else if (is_gio_error (error, G_IO_ERROR_PERMISSION_DENIED)) + { + message_details = g_strdup (_("You do not have the permissions necessary to open the file.")); + } + else if ((is_gio_error (error, G_IO_ERROR_INVALID_DATA) && encoding == NULL) || + (error->domain == GTK_SOURCE_FILE_LOADER_ERROR && + error->code == GTK_SOURCE_FILE_LOADER_ERROR_ENCODING_AUTO_DETECTION_FAILED)) + { + message_details = g_strconcat (_("Unable to detect the character encoding."), "\n", + _("Please check that you are not trying to open a binary file."), "\n", + _("Select a character encoding from the menu and try again."), NULL); + convert_error = TRUE; + } + else if (error->domain == GTK_SOURCE_FILE_LOADER_ERROR && + error->code == GTK_SOURCE_FILE_LOADER_ERROR_CONVERSION_FALLBACK) + { + error_message = g_strdup_printf (_("There was a problem opening the file “%s”."), + uri_for_display); + message_details = g_strconcat (_("The file you opened has some invalid characters. " + "If you continue editing this file you could corrupt this " + "document."), "\n", +@@ -578,219 +585,405 @@ gedit_io_loading_error_info_bar_new (GFile *location, + g_free (message_details); + + return info_bar; + } + + GtkWidget * + gedit_conversion_error_while_saving_info_bar_new (GFile *location, + const GtkSourceEncoding *encoding, + const GError *error) + { + gchar *error_message = NULL; + gchar *message_details = NULL; + gchar *full_formatted_uri; + gchar *encoding_name; + gchar *uri_for_display; + gchar *temp_uri_for_display; + GtkWidget *info_bar; + + g_return_val_if_fail (G_IS_FILE (location), NULL); + g_return_val_if_fail (error != NULL, NULL); + g_return_val_if_fail (error->domain == G_CONVERT_ERROR || + error->domain == G_IO_ERROR, NULL); + g_return_val_if_fail (encoding != NULL, NULL); + + full_formatted_uri = g_file_get_parse_name (location); + + /* Truncate the URI so it doesn't get insanely wide. Note that even + * though the dialog uses wrapped text, if the URI doesn't contain + * white space then the text-wrapping code is too stupid to wrap it. + */ +- temp_uri_for_display = tepl_utils_str_middle_truncate (full_formatted_uri, +- MAX_URI_IN_DIALOG_LENGTH); ++ temp_uri_for_display = gedit_utils_str_middle_truncate (full_formatted_uri, ++ MAX_URI_IN_DIALOG_LENGTH); + g_free (full_formatted_uri); + + uri_for_display = g_markup_escape_text (temp_uri_for_display, -1); + g_free (temp_uri_for_display); + + encoding_name = gtk_source_encoding_to_string (encoding); + + error_message = g_strdup_printf (_("Could not save the file “%s” using the “%s” character encoding."), + uri_for_display, + encoding_name); + message_details = g_strconcat (_("The document contains one or more characters that cannot be encoded " + "using the specified character encoding."), "\n", + _("Select a different character encoding from the menu and try again."), NULL); + + info_bar = create_conversion_error_info_bar (error_message, + message_details, + FALSE); + + g_free (uri_for_display); + g_free (encoding_name); + g_free (error_message); + g_free (message_details); + + return info_bar; + } + + const GtkSourceEncoding * + gedit_conversion_error_info_bar_get_encoding (GtkWidget *info_bar) + { + gpointer menu; + + g_return_val_if_fail (GTK_IS_INFO_BAR (info_bar), NULL); + + menu = g_object_get_data (G_OBJECT (info_bar), + "gedit-info-bar-encoding-menu"); + if (menu != NULL) + { + return gedit_encodings_combo_box_get_selected_encoding (GEDIT_ENCODINGS_COMBO_BOX (menu)); + } + + return NULL; + } + ++GtkWidget * ++gedit_file_already_open_warning_info_bar_new (GFile *location) ++{ ++ GtkWidget *info_bar; ++ GtkWidget *hbox_content; ++ GtkWidget *vbox; ++ gchar *primary_markup; ++ gchar *secondary_markup; ++ GtkWidget *primary_label; ++ GtkWidget *secondary_label; ++ gchar *primary_text; ++ const gchar *secondary_text; ++ gchar *full_formatted_uri; ++ gchar *uri_for_display; ++ gchar *temp_uri_for_display; ++ ++ g_return_val_if_fail (G_IS_FILE (location), NULL); ++ ++ full_formatted_uri = g_file_get_parse_name (location); ++ ++ /* Truncate the URI so it doesn't get insanely wide. Note that even ++ * though the dialog uses wrapped text, if the URI doesn't contain ++ * white space then the text-wrapping code is too stupid to wrap it. ++ */ ++ temp_uri_for_display = gedit_utils_str_middle_truncate (full_formatted_uri, ++ MAX_URI_IN_DIALOG_LENGTH); ++ g_free (full_formatted_uri); ++ ++ uri_for_display = g_markup_escape_text (temp_uri_for_display, -1); ++ g_free (temp_uri_for_display); ++ ++ info_bar = gtk_info_bar_new (); ++ gtk_info_bar_add_button (GTK_INFO_BAR (info_bar), ++ /* Translators: the access key chosen for this string should be ++ different from other main menu access keys (Open, Edit, View...) */ ++ _("Edit Any_way"), ++ GTK_RESPONSE_YES); ++ gtk_info_bar_add_button (GTK_INFO_BAR (info_bar), ++ /* Translators: the access key chosen for this string should be ++ different from other main menu access keys (Open, Edit, View...) */ ++ _("D_on’t Edit"), ++ GTK_RESPONSE_CANCEL); ++ gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), ++ GTK_MESSAGE_WARNING); ++ ++ hbox_content = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8); ++ ++ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); ++ gtk_box_pack_start (GTK_BOX (hbox_content), vbox, TRUE, TRUE, 0); ++ ++ primary_text = g_strdup_printf (_("This file “%s” is already open in another window."), uri_for_display); ++ g_free (uri_for_display); ++ ++ primary_markup = g_strdup_printf ("%s", primary_text); ++ g_free (primary_text); ++ primary_label = gtk_label_new (primary_markup); ++ g_free (primary_markup); ++ gtk_box_pack_start (GTK_BOX (vbox), primary_label, TRUE, TRUE, 0); ++ gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE); ++ gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE); ++ gtk_widget_set_halign (primary_label, GTK_ALIGN_START); ++ gtk_widget_set_can_focus (primary_label, TRUE); ++ gtk_label_set_selectable (GTK_LABEL (primary_label), TRUE); ++ ++ secondary_text = _("Do you want to edit it anyway?"); ++ secondary_markup = g_strdup_printf ("%s", ++ secondary_text); ++ secondary_label = gtk_label_new (secondary_markup); ++ g_free (secondary_markup); ++ gtk_box_pack_start (GTK_BOX (vbox), secondary_label, TRUE, TRUE, 0); ++ gtk_widget_set_can_focus (secondary_label, TRUE); ++ gtk_label_set_use_markup (GTK_LABEL (secondary_label), TRUE); ++ gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE); ++ gtk_label_set_selectable (GTK_LABEL (secondary_label), TRUE); ++ gtk_widget_set_halign (secondary_label, GTK_ALIGN_START); ++ ++ gtk_widget_show_all (hbox_content); ++ set_contents (info_bar, hbox_content); ++ ++ return info_bar; ++} ++ + GtkWidget * + gedit_externally_modified_saving_error_info_bar_new (GFile *location, + const GError *error) + { + GtkWidget *info_bar; + GtkWidget *hbox_content; + GtkWidget *vbox; + gchar *primary_markup; + gchar *secondary_markup; + GtkWidget *primary_label; + GtkWidget *secondary_label; + gchar *primary_text; + const gchar *secondary_text; + gchar *full_formatted_uri; + gchar *uri_for_display; + gchar *temp_uri_for_display; + + g_return_val_if_fail (G_IS_FILE (location), NULL); + g_return_val_if_fail (error != NULL, NULL); + g_return_val_if_fail (error->domain == GTK_SOURCE_FILE_SAVER_ERROR, NULL); + g_return_val_if_fail (error->code == GTK_SOURCE_FILE_SAVER_ERROR_EXTERNALLY_MODIFIED, NULL); + + full_formatted_uri = g_file_get_parse_name (location); + + /* Truncate the URI so it doesn't get insanely wide. Note that even + * though the dialog uses wrapped text, if the URI doesn't contain + * white space then the text-wrapping code is too stupid to wrap it. + */ +- temp_uri_for_display = tepl_utils_str_middle_truncate (full_formatted_uri, +- MAX_URI_IN_DIALOG_LENGTH); ++ temp_uri_for_display = gedit_utils_str_middle_truncate (full_formatted_uri, ++ MAX_URI_IN_DIALOG_LENGTH); + g_free (full_formatted_uri); + + uri_for_display = g_markup_escape_text (temp_uri_for_display, -1); + g_free (temp_uri_for_display); + + info_bar = gtk_info_bar_new (); + + gtk_info_bar_add_button (GTK_INFO_BAR (info_bar), + _("S_ave Anyway"), + GTK_RESPONSE_YES); + gtk_info_bar_add_button (GTK_INFO_BAR (info_bar), + _("D_on’t Save"), + GTK_RESPONSE_CANCEL); + gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), + GTK_MESSAGE_WARNING); + + hbox_content = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + gtk_box_pack_start (GTK_BOX (hbox_content), vbox, TRUE, TRUE, 0); + + /* FIXME: review this message, it's not clear since for the user the "modification" + * could be interpreted as the changes he made in the document. beside "reading" is + * not accurate (since last load/save) + */ + primary_text = g_strdup_printf (_("The file “%s” has been modified since reading it."), + uri_for_display); + g_free (uri_for_display); + + primary_markup = g_strdup_printf ("%s", primary_text); + g_free (primary_text); + primary_label = gtk_label_new (primary_markup); + g_free (primary_markup); + gtk_box_pack_start (GTK_BOX (vbox), primary_label, TRUE, TRUE, 0); + gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE); + gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE); + gtk_widget_set_halign (primary_label, GTK_ALIGN_START); + gtk_widget_set_can_focus (primary_label, TRUE); + gtk_label_set_selectable (GTK_LABEL (primary_label), TRUE); + + secondary_text = _("If you save it, all the external changes could be lost. Save it anyway?"); + secondary_markup = g_strdup_printf ("%s", + secondary_text); + secondary_label = gtk_label_new (secondary_markup); + g_free (secondary_markup); + gtk_box_pack_start (GTK_BOX (vbox), secondary_label, TRUE, TRUE, 0); + gtk_widget_set_can_focus (secondary_label, TRUE); + gtk_label_set_use_markup (GTK_LABEL (secondary_label), TRUE); + gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE); + gtk_label_set_selectable (GTK_LABEL (secondary_label), TRUE); + gtk_widget_set_halign (secondary_label, GTK_ALIGN_START); + + gtk_widget_show_all (hbox_content); + set_contents (info_bar, hbox_content); + + return info_bar; + } + ++GtkWidget * ++gedit_no_backup_saving_error_info_bar_new (GFile *location, ++ const GError *error) ++{ ++ GtkWidget *info_bar; ++ GtkWidget *hbox_content; ++ GtkWidget *vbox; ++ gchar *primary_markup; ++ gchar *secondary_markup; ++ GtkWidget *primary_label; ++ GtkWidget *secondary_label; ++ gchar *primary_text; ++ const gchar *secondary_text; ++ gchar *full_formatted_uri; ++ gchar *uri_for_display; ++ gchar *temp_uri_for_display; ++ gboolean create_backup_copy; ++ GSettings *editor_settings; ++ ++ g_return_val_if_fail (G_IS_FILE (location), NULL); ++ g_return_val_if_fail (error != NULL, NULL); ++ g_return_val_if_fail (error->domain == G_IO_ERROR && ++ error->code == G_IO_ERROR_CANT_CREATE_BACKUP, NULL); ++ ++ full_formatted_uri = g_file_get_parse_name (location); ++ ++ /* Truncate the URI so it doesn't get insanely wide. Note that even ++ * though the dialog uses wrapped text, if the URI doesn't contain ++ * white space then the text-wrapping code is too stupid to wrap it. ++ */ ++ temp_uri_for_display = gedit_utils_str_middle_truncate (full_formatted_uri, ++ MAX_URI_IN_DIALOG_LENGTH); ++ g_free (full_formatted_uri); ++ ++ uri_for_display = g_markup_escape_text (temp_uri_for_display, -1); ++ g_free (temp_uri_for_display); ++ ++ info_bar = gtk_info_bar_new (); ++ ++ gtk_info_bar_add_button (GTK_INFO_BAR (info_bar), ++ _("S_ave Anyway"), ++ GTK_RESPONSE_YES); ++ gtk_info_bar_add_button (GTK_INFO_BAR (info_bar), ++ _("D_on’t Save"), ++ GTK_RESPONSE_CANCEL); ++ gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), ++ GTK_MESSAGE_WARNING); ++ ++ hbox_content = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8); ++ ++ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); ++ gtk_box_pack_start (GTK_BOX (hbox_content), vbox, TRUE, TRUE, 0); ++ ++ editor_settings = g_settings_new ("org.gnome.gedit.preferences.editor"); ++ ++ create_backup_copy = g_settings_get_boolean (editor_settings, ++ GEDIT_SETTINGS_CREATE_BACKUP_COPY); ++ g_object_unref (editor_settings); ++ ++ /* FIXME: review this messages */ ++ if (create_backup_copy) ++ { ++ primary_text = g_strdup_printf (_("Could not create a backup file while saving “%s”"), ++ uri_for_display); ++ } ++ else ++ { ++ primary_text = g_strdup_printf (_("Could not create a temporary backup file while saving “%s”"), ++ uri_for_display); ++ } ++ ++ g_free (uri_for_display); ++ ++ primary_markup = g_strdup_printf ("%s", primary_text); ++ g_free (primary_text); ++ primary_label = gtk_label_new (primary_markup); ++ g_free (primary_markup); ++ gtk_box_pack_start (GTK_BOX (vbox), primary_label, TRUE, TRUE, 0); ++ gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE); ++ gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE); ++ gtk_widget_set_halign (primary_label, GTK_ALIGN_START); ++ gtk_widget_set_can_focus (primary_label, TRUE); ++ gtk_label_set_selectable (GTK_LABEL (primary_label), TRUE); ++ ++ secondary_text = _("Could not back up the old copy of the file before saving the new one. " ++ "You can ignore this warning and save the file anyway, but if an error " ++ "occurs while saving, you could lose the old copy of the file. Save anyway?"); ++ secondary_markup = g_strdup_printf ("%s", ++ secondary_text); ++ secondary_label = gtk_label_new (secondary_markup); ++ g_free (secondary_markup); ++ gtk_box_pack_start (GTK_BOX (vbox), secondary_label, TRUE, TRUE, 0); ++ gtk_widget_set_can_focus (secondary_label, TRUE); ++ gtk_label_set_use_markup (GTK_LABEL (secondary_label), TRUE); ++ gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE); ++ gtk_label_set_selectable (GTK_LABEL (secondary_label), TRUE); ++ gtk_widget_set_halign (secondary_label, GTK_ALIGN_START); ++ ++ gtk_widget_show_all (hbox_content); ++ set_contents (info_bar, hbox_content); ++ ++ return info_bar; ++} ++ + GtkWidget * + gedit_unrecoverable_saving_error_info_bar_new (GFile *location, + const GError *error) + { + gchar *error_message = NULL; + gchar *message_details = NULL; + gchar *full_formatted_uri; + gchar *scheme_string; + gchar *scheme_markup; + gchar *uri_for_display; + gchar *temp_uri_for_display; + GtkWidget *info_bar; + + g_return_val_if_fail (G_IS_FILE (location), NULL); + g_return_val_if_fail (error != NULL, NULL); + g_return_val_if_fail (error->domain == GTK_SOURCE_FILE_SAVER_ERROR || + error->domain == G_IO_ERROR, NULL); + + full_formatted_uri = g_file_get_parse_name (location); + + /* Truncate the URI so it doesn't get insanely wide. Note that even + * though the dialog uses wrapped text, if the URI doesn't contain + * white space then the text-wrapping code is too stupid to wrap it. + */ +- temp_uri_for_display = tepl_utils_str_middle_truncate (full_formatted_uri, +- MAX_URI_IN_DIALOG_LENGTH); ++ temp_uri_for_display = gedit_utils_str_middle_truncate (full_formatted_uri, ++ MAX_URI_IN_DIALOG_LENGTH); + g_free (full_formatted_uri); + + uri_for_display = g_markup_escape_text (temp_uri_for_display, -1); + g_free (temp_uri_for_display); + + if (is_gio_error (error, G_IO_ERROR_NOT_SUPPORTED)) + { + scheme_string = g_file_get_uri_scheme (location); + + if ((scheme_string != NULL) && g_utf8_validate (scheme_string, -1, NULL)) + { + scheme_markup = g_markup_escape_text (scheme_string, -1); + + /* Translators: %s is a URI scheme (like for example http:, ftp:, etc.) */ + message_details = g_strdup_printf (_("Cannot handle “%s:” locations in write mode. " + "Please check that you typed the " + "location correctly and try again."), + scheme_markup); + g_free (scheme_markup); + } + else + { + message_details = g_strdup (_("Cannot handle this location in write mode. " + "Please check that you typed the " + "location correctly and try again.")); + } + + g_free (scheme_string); + } + else if (is_gio_error (error, G_IO_ERROR_INVALID_FILENAME)) +@@ -840,31 +1033,176 @@ gedit_unrecoverable_saving_error_info_bar_new (GFile *location, + "a smaller file or saving it to a disk that does not " + "have this limitation.")); + } + #endif + else + { + parse_error (error, + &error_message, + &message_details, + location, + uri_for_display); + } + + if (error_message == NULL) + { + error_message = g_strdup_printf (_("Could not save the file “%s”."), + uri_for_display); + } + + info_bar = create_io_loading_error_info_bar (error_message, + message_details, + FALSE); + + g_free (uri_for_display); + g_free (error_message); + g_free (message_details); + + return info_bar; + } + ++GtkWidget * ++gedit_externally_modified_info_bar_new (GFile *location, ++ gboolean document_modified) ++{ ++ gchar *full_formatted_uri; ++ gchar *uri_for_display; ++ gchar *temp_uri_for_display; ++ gchar *primary_text; ++ GtkWidget *info_bar; ++ ++ g_return_val_if_fail (G_IS_FILE (location), NULL); ++ ++ full_formatted_uri = g_file_get_parse_name (location); ++ ++ /* Truncate the URI so it doesn't get insanely wide. Note that even ++ * though the dialog uses wrapped text, if the URI doesn't contain ++ * white space then the text-wrapping code is too stupid to wrap it. ++ */ ++ temp_uri_for_display = gedit_utils_str_middle_truncate (full_formatted_uri, ++ MAX_URI_IN_DIALOG_LENGTH); ++ g_free (full_formatted_uri); ++ ++ uri_for_display = g_markup_escape_text (temp_uri_for_display, -1); ++ g_free (temp_uri_for_display); ++ ++ primary_text = g_strdup_printf (_("The file “%s” changed on disk."), ++ uri_for_display); ++ g_free (uri_for_display); ++ ++ info_bar = gtk_info_bar_new (); ++ ++ if (document_modified) ++ { ++ GtkWidget *box; ++ GtkWidget *button; ++ button = gtk_info_bar_add_button (GTK_INFO_BAR (info_bar), ++ _("Drop Changes and _Reload"), ++ GTK_RESPONSE_OK); ++ box = gtk_info_bar_get_action_area (GTK_INFO_BAR (info_bar)); ++ gtk_button_box_set_child_non_homogeneous (GTK_BUTTON_BOX (box), ++ button, ++ TRUE); ++ } ++ else ++ { ++ gtk_info_bar_add_button (GTK_INFO_BAR (info_bar), ++ _("_Reload"), ++ GTK_RESPONSE_OK); ++ } ++ ++ gtk_info_bar_set_show_close_button (GTK_INFO_BAR (info_bar), TRUE); ++ gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), ++ GTK_MESSAGE_WARNING); ++ ++ set_info_bar_text (info_bar, ++ primary_text, ++ NULL); ++ ++ g_free (primary_text); ++ ++ return info_bar; ++} ++ ++GtkWidget * ++gedit_invalid_character_info_bar_new (GFile *location) ++{ ++ GtkWidget *info_bar; ++ GtkWidget *hbox_content; ++ GtkWidget *vbox; ++ GtkWidget *primary_label; ++ GtkWidget *secondary_label; ++ gchar *primary_markup; ++ gchar *secondary_markup; ++ gchar *primary_text; ++ gchar *full_formatted_uri; ++ gchar *uri_for_display; ++ gchar *temp_uri_for_display; ++ const gchar *secondary_text; ++ ++ g_return_val_if_fail (G_IS_FILE (location), NULL); ++ ++ full_formatted_uri = g_file_get_parse_name (location); ++ ++ /* Truncate the URI so it doesn't get insanely wide. Note that even ++ * though the dialog uses wrapped text, if the URI doesn't contain ++ * white space then the text-wrapping code is too stupid to wrap it. ++ */ ++ temp_uri_for_display = gedit_utils_str_middle_truncate (full_formatted_uri, ++ MAX_URI_IN_DIALOG_LENGTH); ++ g_free (full_formatted_uri); ++ ++ uri_for_display = g_markup_escape_text (temp_uri_for_display, -1); ++ g_free (temp_uri_for_display); ++ ++ info_bar = gtk_info_bar_new (); ++ ++ gtk_info_bar_add_button (GTK_INFO_BAR (info_bar), ++ _("S_ave Anyway"), ++ GTK_RESPONSE_YES); ++ gtk_info_bar_add_button (GTK_INFO_BAR (info_bar), ++ _("D_on’t Save"), ++ GTK_RESPONSE_CANCEL); ++ gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), ++ GTK_MESSAGE_WARNING); ++ ++ hbox_content = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8); ++ ++ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); ++ gtk_box_pack_start (GTK_BOX (hbox_content), vbox, TRUE, TRUE, 0); ++ ++ primary_text = g_strdup_printf (_("Some invalid chars have been detected while saving “%s”"), ++ uri_for_display); ++ ++ g_free (uri_for_display); ++ ++ primary_markup = g_strdup_printf ("%s", primary_text); ++ g_free (primary_text); ++ primary_label = gtk_label_new (primary_markup); ++ g_free (primary_markup); ++ gtk_box_pack_start (GTK_BOX (vbox), primary_label, TRUE, TRUE, 0); ++ gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE); ++ gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE); ++ gtk_widget_set_halign (primary_label, GTK_ALIGN_START); ++ gtk_widget_set_can_focus (primary_label, TRUE); ++ gtk_label_set_selectable (GTK_LABEL (primary_label), TRUE); ++ ++ secondary_text = _("If you continue saving this file you can corrupt the document. " ++ " Save anyway?"); ++ secondary_markup = g_strdup_printf ("%s", ++ secondary_text); ++ secondary_label = gtk_label_new (secondary_markup); ++ g_free (secondary_markup); ++ gtk_box_pack_start (GTK_BOX (vbox), secondary_label, TRUE, TRUE, 0); ++ gtk_widget_set_can_focus (secondary_label, TRUE); ++ gtk_label_set_use_markup (GTK_LABEL (secondary_label), TRUE); ++ gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE); ++ gtk_label_set_selectable (GTK_LABEL (secondary_label), TRUE); ++ gtk_widget_set_halign (secondary_label, GTK_ALIGN_START); ++ ++ gtk_widget_show_all (hbox_content); ++ set_contents (info_bar, hbox_content); ++ ++ return info_bar; ++} ++ + /* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-io-error-info-bar.h b/gedit/gedit-io-error-info-bar.h +index 12780f7ae..9784652c7 100644 +--- a/gedit/gedit-io-error-info-bar.h ++++ b/gedit/gedit-io-error-info-bar.h +@@ -12,41 +12,51 @@ + * 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, see . + */ + + #ifndef GEDIT_IO_ERROR_INFO_BAR_H + #define GEDIT_IO_ERROR_INFO_BAR_H + + #include + + G_BEGIN_DECLS + + GtkWidget *gedit_io_loading_error_info_bar_new (GFile *location, + const GtkSourceEncoding *encoding, + const GError *error); + + GtkWidget *gedit_unrecoverable_reverting_error_info_bar_new (GFile *location, + const GError *error); + + GtkWidget *gedit_conversion_error_while_saving_info_bar_new (GFile *location, + const GtkSourceEncoding *encoding, + const GError *error); + + const GtkSourceEncoding + *gedit_conversion_error_info_bar_get_encoding (GtkWidget *info_bar); + ++GtkWidget *gedit_file_already_open_warning_info_bar_new (GFile *location); ++ + GtkWidget *gedit_externally_modified_saving_error_info_bar_new (GFile *location, + const GError *error); + ++GtkWidget *gedit_no_backup_saving_error_info_bar_new (GFile *location, ++ const GError *error); ++ + GtkWidget *gedit_unrecoverable_saving_error_info_bar_new (GFile *location, + const GError *error); + ++GtkWidget *gedit_externally_modified_info_bar_new (GFile *location, ++ gboolean document_modified); ++ ++GtkWidget *gedit_invalid_character_info_bar_new (GFile *location); ++ + G_END_DECLS + + #endif /* GEDIT_IO_ERROR_INFO_BAR_H */ + + /* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-metadata-manager.c b/gedit/gedit-metadata-manager.c +new file mode 100644 +index 000000000..8f858b286 +--- /dev/null ++++ b/gedit/gedit-metadata-manager.c +@@ -0,0 +1,650 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ ++/* ++ * gedit-metadata-manager.c ++ * This file is part of gedit ++ * ++ * Copyright (C) 2003-2007 Paolo Maggi ++ * Copyright (C) 2019 Canonical LTD ++ * ++ * 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, see . ++ */ ++ ++#include "gedit-metadata-manager.h" ++#include ++#include "gedit-debug.h" ++ ++/* ++#define GEDIT_METADATA_VERBOSE_DEBUG 1 ++*/ ++ ++#define MAX_ITEMS 50 ++ ++typedef struct _Item Item; ++ ++struct _Item ++{ ++ /* Time of last access in seconds since January 1, 1970 UTC. */ ++ gint64 atime; ++ ++ GHashTable *values; ++}; ++ ++struct _GeditMetadataManager ++{ ++ GObject parent_instance; ++ ++ /* It is true if the file has been read. */ ++ gboolean values_loaded; ++ ++ guint timeout_id; ++ ++ GHashTable *items; ++ ++ gchar *metadata_filename; ++}; ++ ++enum ++{ ++ PROP_0, ++ PROP_METADATA_FILENAME, ++ LAST_PROP ++}; ++ ++static GParamSpec *properties[LAST_PROP]; ++ ++G_DEFINE_TYPE (GeditMetadataManager, gedit_metadata_manager, G_TYPE_OBJECT); ++ ++static gboolean gedit_metadata_manager_save (GeditMetadataManager *self); ++ ++static void ++item_free (gpointer data) ++{ ++ Item *item; ++ ++ g_return_if_fail (data != NULL); ++ ++#ifdef GEDIT_METADATA_VERBOSE_DEBUG ++ gedit_debug (DEBUG_METADATA); ++#endif ++ ++ item = (Item *)data; ++ ++ if (item->values != NULL) ++ g_hash_table_destroy (item->values); ++ ++ g_free (item); ++} ++ ++static void ++gedit_metadata_manager_arm_timeout (GeditMetadataManager *self) ++{ ++ if (self->timeout_id == 0) ++ { ++ self->timeout_id = ++ g_timeout_add_seconds_full (G_PRIORITY_DEFAULT_IDLE, ++ 2, ++ (GSourceFunc)gedit_metadata_manager_save, ++ self, ++ NULL); ++ } ++} ++ ++static void ++gedit_metadata_manager_parse_item (GeditMetadataManager *self, ++ xmlDocPtr doc, ++ xmlNodePtr cur) ++{ ++ Item *item; ++ ++ xmlChar *uri; ++ xmlChar *atime; ++ ++#ifdef GEDIT_METADATA_VERBOSE_DEBUG ++ gedit_debug (DEBUG_METADATA); ++#endif ++ ++ if (xmlStrcmp (cur->name, (const xmlChar *)"document") != 0) ++ return; ++ ++ uri = xmlGetProp (cur, (const xmlChar *)"uri"); ++ if (uri == NULL) ++ return; ++ ++ atime = xmlGetProp (cur, (const xmlChar *)"atime"); ++ if (atime == NULL) ++ { ++ xmlFree (uri); ++ return; ++ } ++ ++ item = g_new0 (Item, 1); ++ ++ item->atime = g_ascii_strtoll ((char *)atime, NULL, 0); ++ ++ item->values = g_hash_table_new_full (g_str_hash, ++ g_str_equal, ++ g_free, ++ g_free); ++ ++ cur = cur->xmlChildrenNode; ++ ++ while (cur != NULL) ++ { ++ if (xmlStrcmp (cur->name, (const xmlChar *)"entry") == 0) ++ { ++ xmlChar *key; ++ xmlChar *value; ++ ++ key = xmlGetProp (cur, (const xmlChar *)"key"); ++ value = xmlGetProp (cur, (const xmlChar *)"value"); ++ ++ if ((key != NULL) && (value != NULL)) ++ { ++ g_hash_table_insert (item->values, ++ g_strdup ((gchar *)key), ++ g_strdup ((gchar *)value)); ++ } ++ ++ if (key != NULL) ++ xmlFree (key); ++ if (value != NULL) ++ xmlFree (value); ++ } ++ ++ cur = cur->next; ++ } ++ ++ g_hash_table_insert (self->items, ++ g_strdup ((gchar *)uri), ++ item); ++ ++ xmlFree (uri); ++ xmlFree (atime); ++} ++ ++/* Returns FALSE in case of error. */ ++static gboolean ++gedit_metadata_manager_load_values (GeditMetadataManager *self) ++{ ++ xmlDocPtr doc; ++ xmlNodePtr cur; ++ ++ gedit_debug (DEBUG_METADATA); ++ ++ g_return_val_if_fail (self != NULL, FALSE); ++ g_return_val_if_fail (self->values_loaded == FALSE, FALSE); ++ ++ self->values_loaded = TRUE; ++ ++ xmlKeepBlanksDefault (0); ++ ++ if (self->metadata_filename == NULL) ++ { ++ return FALSE; ++ } ++ ++ /* TODO: avoid races */ ++ if (!g_file_test (self->metadata_filename, G_FILE_TEST_EXISTS)) ++ { ++ return TRUE; ++ } ++ ++ doc = xmlParseFile (self->metadata_filename); ++ ++ if (doc == NULL) ++ { ++ return FALSE; ++ } ++ ++ cur = xmlDocGetRootElement (doc); ++ if (cur == NULL) ++ { ++ g_message ("The metadata file '%s' is empty", ++ g_path_get_basename (self->metadata_filename)); ++ xmlFreeDoc (doc); ++ ++ return TRUE; ++ } ++ ++ if (xmlStrcmp (cur->name, (const xmlChar *) "metadata")) ++ { ++ g_message ("File '%s' is of the wrong type", ++ g_path_get_basename (self->metadata_filename)); ++ xmlFreeDoc (doc); ++ ++ return FALSE; ++ } ++ ++ cur = xmlDocGetRootElement (doc); ++ cur = cur->xmlChildrenNode; ++ ++ while (cur != NULL) ++ { ++ gedit_metadata_manager_parse_item (self, doc, cur); ++ ++ cur = cur->next; ++ } ++ ++ xmlFreeDoc (doc); ++ ++ return TRUE; ++} ++ ++/** ++ * gedit_metadata_manager_get: ++ * @self: a #GeditMetadataManager. ++ * @location: a #GFile. ++ * @key: a key. ++ * ++ * Gets the value associated with the specified @key for the file @location. ++ */ ++gchar * ++gedit_metadata_manager_get (GeditMetadataManager *self, ++ GFile *location, ++ const gchar *key) ++{ ++ Item *item; ++ gchar *value; ++ gchar *uri; ++ ++ g_return_val_if_fail (GEDIT_IS_METADATA_MANAGER (self), NULL); ++ g_return_val_if_fail (G_IS_FILE (location), NULL); ++ g_return_val_if_fail (key != NULL, NULL); ++ ++ uri = g_file_get_uri (location); ++ ++ gedit_debug_message (DEBUG_METADATA, "URI: %s --- key: %s", uri, key ); ++ ++ if (!self->values_loaded) ++ { ++ gboolean res; ++ ++ res = gedit_metadata_manager_load_values (self); ++ ++ if (!res) ++ { ++ g_free (uri); ++ return NULL; ++ } ++ } ++ ++ item = (Item *)g_hash_table_lookup (self->items, uri); ++ ++ g_free (uri); ++ ++ if (item == NULL) ++ return NULL; ++ ++ item->atime = g_get_real_time () / 1000; ++ ++ if (item->values == NULL) ++ return NULL; ++ ++ value = g_hash_table_lookup (item->values, key); ++ ++ if (value == NULL) ++ return NULL; ++ else ++ return g_strdup (value); ++} ++ ++/** ++ * gedit_metadata_manager_set: ++ * @self: a #GeditMetadataManager. ++ * @location: a #GFile. ++ * @key: a key. ++ * @value: the value associated with the @key. ++ * ++ * Sets the @key to contain the given @value for the file @location. ++ */ ++void ++gedit_metadata_manager_set (GeditMetadataManager *self, ++ GFile *location, ++ const gchar *key, ++ const gchar *value) ++{ ++ Item *item; ++ gchar *uri; ++ ++ g_return_if_fail (GEDIT_IS_METADATA_MANAGER (self)); ++ g_return_if_fail (G_IS_FILE (location)); ++ g_return_if_fail (key != NULL); ++ ++ uri = g_file_get_uri (location); ++ ++ gedit_debug_message (DEBUG_METADATA, "URI: %s --- key: %s --- value: %s", uri, key, value); ++ ++ if (!self->values_loaded) ++ { ++ gboolean ok; ++ ++ ok = gedit_metadata_manager_load_values (self); ++ ++ if (!ok) ++ { ++ g_free (uri); ++ return; ++ } ++ } ++ ++ item = (Item *)g_hash_table_lookup (self->items, uri); ++ ++ if (item == NULL) ++ { ++ item = g_new0 (Item, 1); ++ ++ g_hash_table_insert (self->items, ++ g_strdup (uri), ++ item); ++ } ++ ++ if (item->values == NULL) ++ { ++ item->values = g_hash_table_new_full (g_str_hash, ++ g_str_equal, ++ g_free, ++ g_free); ++ } ++ ++ if (value != NULL) ++ { ++ g_hash_table_insert (item->values, ++ g_strdup (key), ++ g_strdup (value)); ++ } ++ else ++ { ++ g_hash_table_remove (item->values, ++ key); ++ } ++ ++ item->atime = g_get_real_time () / 1000; ++ ++ g_free (uri); ++ ++ gedit_metadata_manager_arm_timeout (self); ++} ++ ++static void ++save_values (const gchar *key, const gchar *value, xmlNodePtr parent) ++{ ++ xmlNodePtr xml_node; ++ ++#ifdef GEDIT_METADATA_VERBOSE_DEBUG ++ gedit_debug (DEBUG_METADATA); ++#endif ++ ++ g_return_if_fail (key != NULL); ++ ++ if (value == NULL) ++ return; ++ ++ xml_node = xmlNewChild (parent, ++ NULL, ++ (const xmlChar *)"entry", ++ NULL); ++ ++ xmlSetProp (xml_node, ++ (const xmlChar *)"key", ++ (const xmlChar *)key); ++ xmlSetProp (xml_node, ++ (const xmlChar *)"value", ++ (const xmlChar *)value); ++ ++#ifdef GEDIT_METADATA_VERBOSE_DEBUG ++ gedit_debug_message (DEBUG_METADATA, "entry: %s = %s", key, value); ++#endif ++} ++ ++static void ++save_item (const gchar *key, const gpointer *data, xmlNodePtr parent) ++{ ++ xmlNodePtr xml_node; ++ const Item *item = (const Item *)data; ++ gchar *atime; ++ ++#ifdef GEDIT_METADATA_VERBOSE_DEBUG ++ gedit_debug (DEBUG_METADATA); ++#endif ++ ++ g_return_if_fail (key != NULL); ++ ++ if (item == NULL) ++ return; ++ ++ xml_node = xmlNewChild (parent, NULL, (const xmlChar *)"document", NULL); ++ ++ xmlSetProp (xml_node, (const xmlChar *)"uri", (const xmlChar *)key); ++ ++#ifdef GEDIT_METADATA_VERBOSE_DEBUG ++ gedit_debug_message (DEBUG_METADATA, "uri: %s", key); ++#endif ++ ++ atime = g_strdup_printf ("%" G_GINT64_FORMAT, item->atime); ++ xmlSetProp (xml_node, (const xmlChar *)"atime", (const xmlChar *)atime); ++ ++#ifdef GEDIT_METADATA_VERBOSE_DEBUG ++ gedit_debug_message (DEBUG_METADATA, "atime: %s", atime); ++#endif ++ ++ g_free (atime); ++ ++ g_hash_table_foreach (item->values, ++ (GHFunc)save_values, ++ xml_node); ++} ++ ++static const gchar * ++gedit_metadata_manager_get_oldest (GeditMetadataManager *self) ++{ ++ GHashTableIter iter; ++ gpointer key, value, key_to_remove = NULL; ++ const Item *item_to_remove = NULL; ++ ++ g_hash_table_iter_init (&iter, self->items); ++ while (g_hash_table_iter_next (&iter, &key, &value)) ++ { ++ const Item *item = (const Item *) value; ++ ++ if (key_to_remove == NULL) ++ { ++ key_to_remove = key; ++ item_to_remove = item; ++ } ++ else ++ { ++ g_return_val_if_fail (item_to_remove != NULL, NULL); ++ ++ if (item->atime < item_to_remove->atime) ++ key_to_remove = key; ++ } ++ } ++ ++ return key_to_remove; ++} ++ ++static void ++gedit_metadata_manager_resize_items (GeditMetadataManager *self) ++{ ++ while (g_hash_table_size (self->items) > MAX_ITEMS) ++ { ++ const gchar *key_to_remove; ++ ++ key_to_remove = gedit_metadata_manager_get_oldest (self); ++ g_return_if_fail (key_to_remove != NULL); ++ g_hash_table_remove (self->items, ++ key_to_remove); ++ } ++} ++ ++static gboolean ++gedit_metadata_manager_save (GeditMetadataManager *self) ++{ ++ xmlDocPtr doc; ++ xmlNodePtr root; ++ ++ gedit_debug (DEBUG_METADATA); ++ ++ self->timeout_id = 0; ++ ++ gedit_metadata_manager_resize_items (self); ++ ++ xmlIndentTreeOutput = TRUE; ++ ++ doc = xmlNewDoc ((const xmlChar *)"1.0"); ++ if (doc == NULL) ++ return TRUE; ++ ++ /* Create metadata root */ ++ root = xmlNewDocNode (doc, NULL, (const xmlChar *)"metadata", NULL); ++ xmlDocSetRootElement (doc, root); ++ ++ g_hash_table_foreach (self->items, ++ (GHFunc)save_item, ++ root); ++ ++ /* FIXME: lock file - Paolo */ ++ if (self->metadata_filename != NULL) ++ { ++ gchar *cache_dir; ++ int res; ++ ++ /* make sure the cache dir exists */ ++ cache_dir = g_path_get_dirname (self->metadata_filename); ++ res = g_mkdir_with_parents (cache_dir, 0755); ++ if (res != -1) ++ { ++ xmlSaveFormatFile (self->metadata_filename, ++ doc, ++ 1); ++ } ++ ++ g_free (cache_dir); ++ } ++ ++ xmlFreeDoc (doc); ++ ++ gedit_debug_message (DEBUG_METADATA, "DONE"); ++ ++ return FALSE; ++} ++ ++static void ++gedit_metadata_manager_get_property (GObject *object, ++ guint prop_id, ++ GValue *value, ++ GParamSpec *pspec) ++{ ++ GeditMetadataManager *self = GEDIT_METADATA_MANAGER (object); ++ ++ switch (prop_id) ++ { ++ case PROP_METADATA_FILENAME: ++ g_value_set_string (value, self->metadata_filename); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ ++ ++static void ++gedit_metadata_manager_set_property (GObject *object, ++ guint prop_id, ++ const GValue *value, ++ GParamSpec *pspec) ++{ ++ GeditMetadataManager *self = GEDIT_METADATA_MANAGER (object); ++ ++ switch (prop_id) ++ { ++ case PROP_METADATA_FILENAME: ++ self->metadata_filename = g_value_dup_string (value); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ ++static void ++gedit_metadata_manager_init (GeditMetadataManager *self) ++{ ++ gedit_debug (DEBUG_METADATA); ++ ++ self->values_loaded = FALSE; ++ ++ self->items = ++ g_hash_table_new_full (g_str_hash, ++ g_str_equal, ++ g_free, ++ item_free); ++} ++ ++static void ++gedit_metadata_manager_dispose (GObject *object) ++{ ++ GeditMetadataManager *self = GEDIT_METADATA_MANAGER (object); ++ ++ gedit_debug (DEBUG_METADATA); ++ ++ if (self->timeout_id) ++ { ++ g_source_remove (self->timeout_id); ++ self->timeout_id = 0; ++ gedit_metadata_manager_save (self); ++ } ++ ++ if (self->items != NULL) ++ g_hash_table_destroy (self->items); ++ ++ g_free (self->metadata_filename); ++} ++ ++static void ++gedit_metadata_manager_class_init (GeditMetadataManagerClass *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ ++ object_class->dispose = gedit_metadata_manager_dispose; ++ object_class->get_property = gedit_metadata_manager_get_property; ++ object_class->set_property = gedit_metadata_manager_set_property; ++ ++ /** ++ * GeditMetadataManager:metadata-filename: ++ * ++ * The filename where the metadata is stored. ++ */ ++ properties[PROP_METADATA_FILENAME] = ++ g_param_spec_string ("metadata-filename", ++ "Metadata filename", ++ "The filename where the metadata is stored", ++ NULL, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); ++ ++ g_object_class_install_properties (object_class, LAST_PROP, properties); ++} ++ ++GeditMetadataManager * ++gedit_metadata_manager_new (const gchar *metadata_filename) ++{ ++ gedit_debug (DEBUG_METADATA); ++ ++ return g_object_new (GEDIT_TYPE_METADATA_MANAGER, ++ "metadata-filename", metadata_filename, ++ NULL); ++} ++ ++/* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-metadata-manager.h b/gedit/gedit-metadata-manager.h +new file mode 100644 +index 000000000..49c2f05bf +--- /dev/null ++++ b/gedit/gedit-metadata-manager.h +@@ -0,0 +1,49 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ ++/* ++ * gedit-metadata-manager.h ++ * This file is part of gedit ++ * ++ * Copyright (C) 2003 Paolo Maggi ++ * Copyright (C) 2019 Canonical LTD ++ * ++ * 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, see . ++ */ ++ ++#ifndef GEDIT_METADATA_MANAGER_H ++#define GEDIT_METADATA_MANAGER_H ++ ++#include ++ ++G_BEGIN_DECLS ++ ++#define GEDIT_TYPE_METADATA_MANAGER (gedit_metadata_manager_get_type()) ++ ++G_DECLARE_FINAL_TYPE (GeditMetadataManager, gedit_metadata_manager, GEDIT, METADATA_MANAGER, GObject) ++ ++GeditMetadataManager *gedit_metadata_manager_new (const gchar *metadata_filename); ++ ++gchar *gedit_metadata_manager_get (GeditMetadataManager *self, ++ GFile *location, ++ const gchar *key); ++ ++void gedit_metadata_manager_set (GeditMetadataManager *self, ++ GFile *location, ++ const gchar *key, ++ const gchar *value); ++ ++G_END_DECLS ++ ++#endif /* GEDIT_METADATA_MANAGER_H */ ++ ++/* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-open-document-selector-helper.c b/gedit/gedit-open-document-selector-helper.c +new file mode 100644 +index 000000000..369d12ea2 +--- /dev/null ++++ b/gedit/gedit-open-document-selector-helper.c +@@ -0,0 +1,103 @@ ++/* ++ * gedit-open-document-selector-helper.c ++ * This file is part of gedit ++ * ++ * Copyright (C) 2015 - Sébastien Lafargue ++ * ++ * gedit 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. ++ * ++ * gedit 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 gedit. If not, see . ++ */ ++ ++#include "gedit-open-document-selector-helper.h" ++ ++void ++gedit_open_document_selector_debug_print_list (const gchar *title, ++ GList *fileitem_list) ++{ ++ FileItem *item; ++ GList *l; ++ glong time_sec; ++ glong time_usec; ++ ++ g_print ("%s\n", title); ++ ++ for (l = fileitem_list; l != NULL; l = l->next) ++ { ++ item = (FileItem *)l->data; ++ time_sec = item->access_time.tv_sec; ++ time_usec = item->access_time.tv_usec; ++ ++ g_print ("%ld:%ld uri:%s (%s %s)\n", ++ time_sec, ++ time_usec, ++ item->uri, ++ item->name, ++ item->path); ++ } ++} ++ ++FileItem * ++gedit_open_document_selector_create_fileitem_item (void) ++{ ++ FileItem *item; ++ ++ item = g_slice_new0 (FileItem); ++ ++ return item; ++} ++ ++void ++gedit_open_document_selector_free_fileitem_item (FileItem *item) ++{ ++ g_free (item->uri); ++ g_free (item->name); ++ g_free (item->path); ++ ++ g_slice_free (FileItem, item); ++} ++ ++FileItem * ++gedit_open_document_selector_copy_fileitem_item (FileItem *item) ++{ ++ FileItem *new_item; ++ ++ new_item = gedit_open_document_selector_create_fileitem_item (); ++ ++ new_item->uri = g_strdup (item->uri); ++ new_item->name = g_strdup (item->name); ++ new_item->path = g_strdup (item->path); ++ new_item->access_time = item->access_time; ++ ++ return new_item; ++} ++ ++inline GList * ++gedit_open_document_selector_copy_file_items_list (const GList *file_items_list) ++{ ++ GList *new_file_items_list; ++ ++ new_file_items_list = g_list_copy_deep ((GList *)file_items_list, ++ (GCopyFunc)gedit_open_document_selector_copy_fileitem_item, ++ NULL); ++ ++ return new_file_items_list; ++} ++ ++inline void ++gedit_open_document_selector_free_file_items_list (GList *file_items_list) ++{ ++ g_list_free_full (file_items_list, ++ (GDestroyNotify)gedit_open_document_selector_free_fileitem_item); ++} ++ ++/* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-open-document-selector-helper.h b/gedit/gedit-open-document-selector-helper.h +new file mode 100644 +index 000000000..6feb65408 +--- /dev/null ++++ b/gedit/gedit-open-document-selector-helper.h +@@ -0,0 +1,103 @@ ++/* ++ * gedit-open-document-selector-helper.h ++ * This file is part of gedit ++ * ++ * Copyright (C) 2015 - Sébastien Lafargue ++ * ++ * gedit 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. ++ * ++ * gedit 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 gedit. If not, see . ++ */ ++ ++#ifndef GEDIT_OPEN_DOCUMENT_SELECTOR_HELPER_H ++#define GEDIT_OPEN_DOCUMENT_SELECTOR_HELPER_H ++ ++#include "gedit-open-document-selector.h" ++ ++#include ++ ++G_BEGIN_DECLS ++ ++typedef struct ++{ ++ gchar *uri; ++ gchar *name; ++ gchar *path; ++ GTimeVal access_time; ++} FileItem; ++ ++typedef enum ++{ ++ GEDIT_OPEN_DOCUMENT_SELECTOR_RECENT_FILES_LIST = 0, ++ GEDIT_OPEN_DOCUMENT_SELECTOR_HOME_DIR_LIST, ++ GEDIT_OPEN_DOCUMENT_SELECTOR_DESKTOP_DIR_LIST, ++ GEDIT_OPEN_DOCUMENT_SELECTOR_LOCAL_BOOKMARKS_DIR_LIST, ++ GEDIT_OPEN_DOCUMENT_SELECTOR_FILE_BROWSER_ROOT_DIR_LIST, ++ GEDIT_OPEN_DOCUMENT_SELECTOR_ACTIVE_DOC_DIR_LIST, ++ GEDIT_OPEN_DOCUMENT_SELECTOR_CURRENT_DOCS_LIST, ++ GEDIT_OPEN_DOCUMENT_SELECTOR_LIST_TYPE_NUM_OF_LISTS ++} ListType; ++ ++/* Use #if 1 and rebuild to activate selector debugging and timing */ ++#if 0 ++#define DEBUG_OPEN_DOCUMENT_SELECTOR ++#endif ++ ++#ifdef DEBUG_OPEN_DOCUMENT_SELECTOR ++G_GNUC_UNUSED static const gchar *list_type_string[] = ++{ ++ "RECENT_FILES_LIST", ++ "HOME_DIR_LIST", ++ "DESKTOP_DIR_LIST", ++ "LOCAL_BOOKMARKS_DIR_LIST", ++ "FILE_BROWSER_ROOT_DIR_LIST", ++ "ACTIVE_DOC_DIR_LIST", ++ "CURRENT_DOCS_LIST" ++}; ++ ++#define DEBUG_SELECTOR(x) do { x; } while (0) ++#define DEBUG_SELECTOR_TIMER_DECL G_GNUC_UNUSED GTimer *debug_timer; ++#define DEBUG_SELECTOR_TIMER_NEW debug_timer = g_timer_new (); ++#define DEBUG_SELECTOR_TIMER_DESTROY g_timer_destroy (debug_timer); ++#define DEBUG_SELECTOR_TIMER_GET g_timer_elapsed (debug_timer, NULL) ++#else ++#define DEBUG_SELECTOR(x) ++#define DEBUG_SELECTOR_TIMER_DECL ++#define DEBUG_SELECTOR_TIMER_NEW ++#define DEBUG_SELECTOR_TIMER_DESTROY ++#define DEBUG_SELECTOR_TIMER_GET ++#endif ++ ++typedef struct ++{ ++ GeditOpenDocumentSelector *selector; ++ ListType type; ++} PushMessage; ++ ++void gedit_open_document_selector_debug_print_list (const gchar *title, ++ GList *fileitem_list); ++ ++GList *gedit_open_document_selector_copy_file_items_list (const GList *file_items_list); ++ ++void gedit_open_document_selector_free_file_items_list (GList *file_items_list); ++ ++FileItem *gedit_open_document_selector_create_fileitem_item (void); ++ ++void gedit_open_document_selector_free_fileitem_item (FileItem *item); ++ ++FileItem *gedit_open_document_selector_copy_fileitem_item (FileItem *item); ++ ++G_END_DECLS ++ ++#endif /* GEDIT_OPEN_DOCUMENT_SELECTOR_HELPER_H */ ++ ++/* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-open-document-selector-store.c b/gedit/gedit-open-document-selector-store.c +new file mode 100644 +index 000000000..e3454f12c +--- /dev/null ++++ b/gedit/gedit-open-document-selector-store.c +@@ -0,0 +1,820 @@ ++/* ++ * gedit-open-document-selector-store.c ++ * This file is part of gedit ++ * ++ * Copyright (C) 2015 - Sébastien Lafargue ++ * ++ * gedit 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. ++ * ++ * gedit 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 gedit. If not, see . ++ */ ++ ++/* You need to call gedit_open_document_selector_store_get_default() ++ * to get a singleton #GeditOpenDocumentSelectorStore object. ++ * #GeditOpenDocumentSelectorStore is responsible of managing ++ * the recent files list and computing others lists. ++ * ++ * The lists returned are lists of FileItem structs. ++ * ++ * #GeditOpenDocumentSelectorStore is destroyed automaticly at ++ * the end of your application. ++ * ++ * Call gedit_open_document_selector_store_update_list_async() with ++ * the corresponding ListType, then in your callback, call ++ * gedit_open_document_selector_store_update_list_finish() to get ++ * in return the list of FileItem structs. ++ * ++ * The recent files list can be filtered by calling ++ * gedit_open_document_selector_store_set_filter() ++ * and you can get the actual filter by calling ++ * gedit_open_document_selector_store_get_filter() ++ * ( this is in addition to the text mime type filter) ++ * ++ * The recent files list is not capped by Gedit settings like ++ * in gedit_recent_get_items() but you still can get the limit ++ * by calling gedit_open_document_selector_store_get_recent_limit(). ++ * ++ * The original setting is stored in gsettings at : ++ * org.gnome.gedit.preferences.ui ++ * with the key : max-recents ++ */ ++ ++#include "gedit-open-document-selector-store.h" ++ ++#include ++ ++#include ++#include ++ ++#include ++#include ++ ++#include "gedit-recent.h" ++#include "gedit-utils.h" ++#include "gedit-window.h" ++#include "gedit-debug.h" ++ ++struct _GeditOpenDocumentSelectorStore ++{ ++ GObject parent_instance; ++ ++ GSource *recent_source; ++ ++ GeditRecentConfiguration recent_config; ++ gchar *filter; ++ GList *recent_items; ++ gint recent_config_limit; ++ gboolean recent_items_need_update; ++}; ++ ++G_LOCK_DEFINE_STATIC (recent_files_filter_lock); ++ ++G_DEFINE_TYPE (GeditOpenDocumentSelectorStore, gedit_open_document_selector_store, G_TYPE_OBJECT) ++ ++G_DEFINE_QUARK (gedit-open-document-selector-store-error-quark, ++ gedit_open_document_selector_store_error) ++ ++static GList * ++get_current_docs_list (GeditOpenDocumentSelectorStore *selector_store G_GNUC_UNUSED, ++ GeditOpenDocumentSelector *selector) ++{ ++ GeditWindow *window; ++ GList *docs; ++ GList *l; ++ GFile *file; ++ GFileInfo *info; ++ FileItem *item; ++ GList *file_items_list = NULL; ++ ++ window = gedit_open_document_selector_get_window (selector); ++ ++ docs = gedit_window_get_documents (window); ++ for (l = docs; l != NULL; l = l->next) ++ { ++ file = gtk_source_file_get_location (gedit_document_get_file (l->data)); ++ if (file == NULL) ++ { ++ /* In case of not saved docs */ ++ continue; ++ } ++ ++ info = g_file_query_info (file, ++ "time::access,time::access-usec", ++ G_FILE_QUERY_INFO_NONE, ++ NULL, ++ NULL); ++ if (info == NULL) ++ { ++ continue; ++ } ++ ++ item = gedit_open_document_selector_create_fileitem_item (); ++ ++ item->access_time.tv_sec = g_file_info_get_attribute_uint64 (info, "time::access"); ++ item->access_time.tv_usec = g_file_info_get_attribute_uint32 (info, "time::access-usec"); ++ item->uri = g_file_get_uri (file); ++ ++ file_items_list = g_list_prepend (file_items_list, item); ++ ++ g_object_unref (info); ++ } ++ ++ g_list_free (docs); ++ return file_items_list; ++} ++ ++/* Notice that a content-type attribute must have been query to work */ ++static gboolean ++check_mime_type (GFileInfo *info) ++{ ++ const gchar *content_type; ++ G_GNUC_UNUSED gchar *mime_type; ++ ++ content_type = g_file_info_get_attribute_string (info, "standard::fast-content-type"); ++ if (content_type == NULL) ++ { ++ return FALSE; ++ } ++ ++#ifdef G_OS_WIN32 ++ if (g_content_type_is_a (content_type, "text")) ++ { ++ return TRUE; ++ } ++ ++ mime_type = g_content_type_get_mime_type (content_type); ++ if (mime_type == NULL) ++ { ++ return FALSE; ++ } ++ ++ if (g_strcmp0 (mime_type, "text/plain") == 0) ++ { ++ g_free (mime_type); ++ return TRUE; ++ } ++ ++ g_free (mime_type); ++#else ++ if (g_content_type_is_a (content_type, "text/plain")) ++ { ++ return TRUE; ++ } ++#endif ++ return FALSE; ++} ++ ++static GList * ++get_children_from_dir (GeditOpenDocumentSelectorStore *selector_store G_GNUC_UNUSED, ++ GFile *dir) ++{ ++ GList *file_items_list = NULL; ++ GFileEnumerator *file_enum; ++ GFileInfo *info; ++ GFileType filetype; ++ GFile *file; ++ FileItem *item; ++ gboolean is_text; ++ gboolean is_correct_type; ++ ++ g_return_val_if_fail (G_IS_FILE (dir), NULL); ++ ++ file_enum = g_file_enumerate_children (dir, ++ "standard::name," ++ "standard::type," ++ "standard::fast-content-type," ++ "time::access,time::access-usec", ++ G_FILE_QUERY_INFO_NONE, ++ NULL, ++ NULL); ++ if (file_enum == NULL) ++ { ++ return NULL; ++ } ++ ++ while ((info = g_file_enumerator_next_file (file_enum, NULL, NULL))) ++ { ++ filetype = g_file_info_get_file_type (info); ++ is_text = check_mime_type (info); ++ is_correct_type = (filetype == G_FILE_TYPE_REGULAR || ++ filetype == G_FILE_TYPE_SYMBOLIC_LINK || ++ filetype == G_FILE_TYPE_SHORTCUT); ++ ++ if (is_text && ++ is_correct_type && ++ (file = g_file_enumerator_get_child (file_enum, info)) != NULL) ++ { ++ item = gedit_open_document_selector_create_fileitem_item (); ++ item->uri = g_file_get_uri (file); ++ ++ item->access_time.tv_sec = g_file_info_get_attribute_uint64 (info, "time::access"); ++ item->access_time.tv_usec = g_file_info_get_attribute_uint32 (info, "time::access-usec"); ++ ++ file_items_list = g_list_prepend (file_items_list, item); ++ g_object_unref (file); ++ } ++ ++ g_object_unref (info); ++ } ++ ++ g_file_enumerator_close (file_enum, NULL, NULL); ++ g_object_unref (file_enum); ++ ++ return file_items_list; ++} ++ ++static GList * ++get_active_doc_dir_list (GeditOpenDocumentSelectorStore *selector_store, ++ GeditOpenDocumentSelector *selector) ++{ ++ GeditWindow *window; ++ GeditDocument *active_doc; ++ GtkSourceFile *source_file; ++ GList *file_items_list = NULL; ++ ++ window = gedit_open_document_selector_get_window (selector); ++ ++ active_doc = gedit_window_get_active_document (window); ++ ++ if (active_doc == NULL) ++ { ++ return NULL; ++ } ++ ++ source_file = gedit_document_get_file (active_doc); ++ if (gtk_source_file_is_local (source_file)) ++ { ++ GFile *location; ++ GFile *parent_dir; ++ ++ location = gtk_source_file_get_location (source_file); ++ parent_dir = g_file_get_parent (location); ++ ++ if (parent_dir != NULL) ++ { ++ file_items_list = get_children_from_dir (selector_store, parent_dir); ++ g_object_unref (parent_dir); ++ } ++ } ++ ++ return file_items_list; ++} ++ ++static GFile * ++get_file_browser_root (GeditOpenDocumentSelectorStore *selector_store G_GNUC_UNUSED, ++ GeditOpenDocumentSelector *selector) ++{ ++ GeditWindow *window; ++ GeditMessageBus *bus; ++ GeditMessage *msg; ++ GFile *root = NULL; ++ ++ window = gedit_open_document_selector_get_window (selector); ++ ++ bus = gedit_window_get_message_bus (window); ++ if (gedit_message_bus_is_registered (bus, "/plugins/filebrowser", "get_root")) ++ { ++ msg = gedit_message_bus_send_sync (bus, "/plugins/filebrowser", "get_root", NULL, NULL); ++ g_object_get (msg, "location", &root, NULL); ++ g_object_unref (msg); ++ } ++ ++ return root; ++} ++ ++static GList * ++get_file_browser_root_dir_list (GeditOpenDocumentSelectorStore *selector_store, ++ GeditOpenDocumentSelector *selector) ++{ ++ GFile *root; ++ GList *file_items_list = NULL; ++ ++ root = get_file_browser_root (selector_store, selector); ++ if (root != NULL && g_file_is_native (root)) ++ { ++ file_items_list = get_children_from_dir (selector_store, root); ++ } ++ ++ g_clear_object (&root); ++ return file_items_list; ++} ++ ++/* Taken and adapted from gtk+ gtkbookmarksmanager.c */ ++static GList * ++read_bookmarks_file (GFile *file) ++{ ++ gchar *contents; ++ gchar **lines, *space; ++ GList *uri_list = NULL; ++ gint i; ++ ++ if (!g_file_load_contents (file, NULL, &contents, NULL, NULL, NULL)) ++ { ++ return NULL; ++ } ++ ++ lines = g_strsplit (contents, "\n", -1); ++ ++ for (i = 0; lines[i]; i++) ++ { ++ if (*lines[i] == '\0') ++ { ++ continue; ++ } ++ ++ if (!g_utf8_validate (lines[i], -1, NULL)) ++ { ++ continue; ++ } ++ ++ if ((space = strchr (lines[i], ' ')) != NULL) ++ { ++ space[0] = '\0'; ++ } ++ ++ uri_list = g_list_prepend (uri_list, g_strdup (lines[i])); ++ } ++ ++ g_strfreev (lines); ++ g_free (contents); ++ ++ return uri_list; ++} ++ ++static GList * ++get_local_bookmarks_list (GeditOpenDocumentSelectorStore *selector_store, ++ GeditOpenDocumentSelector *selector G_GNUC_UNUSED) ++{ ++ GList *bookmarks_uri_list = NULL; ++ GList *file_items_list = NULL; ++ GList *new_file_items_list = NULL; ++ GFile *bookmarks_file; ++ GFile *file; ++ gchar *filename; ++ GList *l; ++ ++ filename = g_build_filename (g_get_user_config_dir (), "gtk-3.0", "bookmarks", NULL); ++ bookmarks_file = g_file_new_for_path (filename); ++ g_free (filename); ++ ++ bookmarks_uri_list = read_bookmarks_file (bookmarks_file); ++ g_object_unref (bookmarks_file); ++ ++ for (l = bookmarks_uri_list; l != NULL; l = l->next) ++ { ++ file = g_file_new_for_uri (l->data); ++ if (g_file_is_native (file)) ++ { ++ new_file_items_list = get_children_from_dir (selector_store, file); ++ file_items_list = g_list_concat (file_items_list, new_file_items_list); ++ } ++ ++ g_object_unref (file); ++ } ++ ++ g_list_free_full (bookmarks_uri_list, g_free); ++ return file_items_list; ++} ++ ++/* Taken and adapted from gtk+ gtkplacessidebar.c */ ++static gboolean ++path_is_home_dir (const gchar *path) ++{ ++ GFile *home_dir; ++ GFile *location; ++ const gchar *home_path; ++ gboolean res; ++ ++ home_path = g_get_home_dir (); ++ if (home_path == NULL) ++ { ++ return FALSE; ++ } ++ ++ home_dir = g_file_new_for_path (home_path); ++ location = g_file_new_for_path (path); ++ res = g_file_equal (home_dir, location); ++ ++ g_object_unref (home_dir); ++ g_object_unref (location); ++ ++ return res; ++} ++ ++static GList * ++get_desktop_dir_list (GeditOpenDocumentSelectorStore *selector_store, ++ GeditOpenDocumentSelector *selector G_GNUC_UNUSED) ++{ ++ GList *file_items_list = NULL; ++ const gchar *desktop_dir_name; ++ gchar *desktop_uri; ++ GFile *desktop_file; ++ ++ desktop_dir_name = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP); ++ ++ /* "To disable a directory, point it to the homedir." ++ * See http://freedesktop.org/wiki/Software/xdg-user-dirs ++ */ ++ if (path_is_home_dir (desktop_dir_name)) ++ { ++ return NULL; ++ } ++ ++ desktop_uri = g_strconcat ("file://", desktop_dir_name, NULL); ++ desktop_file = g_file_new_for_uri (desktop_uri); ++ file_items_list = get_children_from_dir (selector_store, desktop_file); ++ ++ g_free (desktop_uri); ++ g_object_unref (desktop_file); ++ ++ return file_items_list; ++} ++ ++static GList * ++get_home_dir_list (GeditOpenDocumentSelectorStore *selector_store, ++ GeditOpenDocumentSelector *selector G_GNUC_UNUSED) ++{ ++ GList *file_items_list = NULL; ++ const gchar *home_name; ++ gchar *home_uri; ++ GFile *home_file; ++ ++ home_name = g_get_home_dir (); ++ if (home_name == NULL) ++ { ++ return NULL; ++ } ++ ++ home_uri = g_strconcat ("file://", home_name, NULL); ++ home_file = g_file_new_for_uri (home_uri); ++ file_items_list = get_children_from_dir (selector_store, home_file); ++ ++ g_free (home_uri); ++ g_object_unref (home_file); ++ ++ return file_items_list; ++} ++ ++static GList * ++convert_recent_item_list_to_fileitem_list (GList *uri_list) ++{ ++ GList *l; ++ GList *fileitem_list = NULL; ++ ++ for (l = uri_list; l != NULL; l = l->next) ++ { ++ gchar *uri; ++ FileItem *item; ++ ++ uri = g_strdup (gtk_recent_info_get_uri (l->data)); ++ ++ item = gedit_open_document_selector_create_fileitem_item (); ++ item->uri = uri; ++ ++ item->access_time.tv_sec = gtk_recent_info_get_visited (l->data); ++ item->access_time.tv_usec = 0; ++ ++ fileitem_list = g_list_prepend (fileitem_list, item); ++ } ++ ++ fileitem_list = g_list_reverse (fileitem_list); ++ return fileitem_list; ++} ++ ++static GList * ++get_recent_files_list (GeditOpenDocumentSelectorStore *selector_store, ++ GeditOpenDocumentSelector *selector G_GNUC_UNUSED) ++{ ++ GList *recent_items_list; ++ GList *file_items_list; ++ ++ G_LOCK (recent_files_filter_lock); ++ recent_items_list = gedit_recent_get_items (&selector_store->recent_config); ++ G_UNLOCK (recent_files_filter_lock); ++ ++ file_items_list = convert_recent_item_list_to_fileitem_list (recent_items_list); ++ g_list_free_full (recent_items_list, (GDestroyNotify)gtk_recent_info_unref); ++ ++ return file_items_list; ++} ++ ++static void ++update_list_cb (GeditOpenDocumentSelectorStore *selector_store, ++ GAsyncResult *res, ++ gpointer user_data G_GNUC_UNUSED) ++{ ++ GList *list; ++ GError *error; ++ PushMessage *message; ++ ListType type; ++ ++ list = gedit_open_document_selector_store_update_list_finish (selector_store, res, &error); ++ ++ message = g_task_get_task_data (G_TASK (res)); ++ type = message->type; ++ ++ switch (type) ++ { ++ case GEDIT_OPEN_DOCUMENT_SELECTOR_RECENT_FILES_LIST: ++ gedit_open_document_selector_free_file_items_list (selector_store->recent_items); ++ selector_store->recent_items = list; ++ ++ DEBUG_SELECTOR (g_print ("\tStore(%p): update_list_cb: type:%s, length:%i\n", ++ selector_store, list_type_string[type], g_list_length (list));); ++ ++ break; ++ default: ++ break; ++ } ++} ++ ++static void ++on_recent_manager_changed (GtkRecentManager *manager G_GNUC_UNUSED, ++ gpointer user_data) ++{ ++ GeditOpenDocumentSelectorStore *selector_store = GEDIT_OPEN_DOCUMENT_SELECTOR_STORE (user_data); ++ ++ selector_store->recent_items_need_update = TRUE; ++ gedit_open_document_selector_store_update_list_async (selector_store, ++ NULL, ++ NULL, ++ (GAsyncReadyCallback)update_list_cb, ++ GEDIT_OPEN_DOCUMENT_SELECTOR_RECENT_FILES_LIST, ++ NULL); ++} ++ ++static void ++gedit_open_document_selector_store_dispose (GObject *object) ++{ ++ GeditOpenDocumentSelectorStore *selector_store = GEDIT_OPEN_DOCUMENT_SELECTOR_STORE (object); ++ ++ gedit_recent_configuration_destroy (&selector_store->recent_config); ++ ++ g_clear_pointer (&selector_store->recent_source, g_source_destroy); ++ g_clear_pointer (&selector_store->filter, g_free); ++ ++ if (selector_store->recent_items) ++ { ++ gedit_open_document_selector_free_file_items_list (selector_store->recent_items); ++ selector_store->recent_items = NULL; ++ } ++ ++ G_OBJECT_CLASS (gedit_open_document_selector_store_parent_class)->dispose (object); ++} ++ ++static void ++gedit_open_document_selector_store_class_init (GeditOpenDocumentSelectorStoreClass *klass) ++{ ++ GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ++ ++ gobject_class->dispose = gedit_open_document_selector_store_dispose; ++} ++ ++/* The order of functions pointers must be the same as in ++ * ListType enum define in ./gedit-open-document-selector-helper.h ++ */ ++static GList * (*list_func [])(GeditOpenDocumentSelectorStore *selector_store, ++ GeditOpenDocumentSelector *selector) = ++{ ++ get_recent_files_list, ++ get_home_dir_list, ++ get_desktop_dir_list, ++ get_local_bookmarks_list, ++ get_file_browser_root_dir_list, ++ get_active_doc_dir_list, ++ get_current_docs_list ++}; ++ ++static gboolean ++update_recent_list (gpointer user_data) ++{ ++ GeditOpenDocumentSelectorStore *selector_store; ++ GeditOpenDocumentSelector *selector; ++ PushMessage *message; ++ /* The type variable is only used when debug code activated */ ++ G_GNUC_UNUSED ListType type; ++ GList *file_items_list; ++ GTask *task = G_TASK(user_data); ++ ++ selector_store = g_task_get_source_object (task); ++ message = g_task_get_task_data (task); ++ selector = message->selector; ++ type = message->type; ++ ++ DEBUG_SELECTOR_TIMER_DECL ++ DEBUG_SELECTOR_TIMER_NEW ++ DEBUG_SELECTOR (g_print ("\tStore(%p): store dispatcher: type:%s\n", ++ selector, list_type_string[type]);); ++ ++ /* Update the recent list only when it changes, copy otherwise but keep it the first time */ ++ if (selector_store->recent_items != NULL && selector_store->recent_items_need_update == FALSE) ++ { ++ file_items_list = gedit_open_document_selector_copy_file_items_list (selector_store->recent_items); ++ ++ DEBUG_SELECTOR (g_print ("\tStore(%p): store dispatcher: recent list copy\n", selector);); ++ } ++ else ++ { ++ selector_store->recent_items_need_update = FALSE; ++ file_items_list = get_recent_files_list (selector_store, selector); ++ ++ DEBUG_SELECTOR (g_print ("\tStore(%p): store dispatcher: recent list compute\n", selector);); ++ ++ if (selector_store->recent_items == NULL) ++ { ++ selector_store->recent_items = gedit_open_document_selector_copy_file_items_list (file_items_list); ++ } ++ } ++ ++ g_task_return_pointer (task, ++ file_items_list, ++ (GDestroyNotify)gedit_open_document_selector_free_file_items_list); ++ ++ DEBUG_SELECTOR (g_print ("\tStore(%p): store dispatcher: type:%s, time:%lf\n", ++ selector, list_type_string[type], DEBUG_SELECTOR_TIMER_GET);); ++ DEBUG_SELECTOR_TIMER_DESTROY ++ ++ selector_store->recent_source = NULL; ++ return G_SOURCE_REMOVE; ++} ++ ++static void ++update_list_dispatcher (GTask *task, ++ gpointer source_object, ++ gpointer task_data, ++ GCancellable *cancellable G_GNUC_UNUSED) ++{ ++ GeditOpenDocumentSelectorStore *selector_store = source_object; ++ GeditOpenDocumentSelector *selector; ++ PushMessage *message; ++ ListType type; ++ GList *file_items_list; ++ ++ message = task_data; ++ selector = message->selector; ++ type = message->type; ++ ++ DEBUG_SELECTOR_TIMER_DECL ++ DEBUG_SELECTOR_TIMER_NEW ++ DEBUG_SELECTOR (g_print ("\tStore(%p): store dispatcher: Thread:%p, type:%s\n", ++ selector, g_thread_self (), list_type_string[type]);); ++ ++ if (type >= GEDIT_OPEN_DOCUMENT_SELECTOR_LIST_TYPE_NUM_OF_LISTS) ++ { ++ g_task_return_new_error (task, ++ GEDIT_OPEN_DOCUMENT_SELECTOR_STORE_ERROR, TYPE_OUT_OF_RANGE, ++ "List Type out of range"); ++ g_object_unref (task); ++ return; ++ } ++ ++ /* Here we call the corresponding list creator function */ ++ file_items_list = (*list_func[type]) (selector_store, selector); ++ ++ DEBUG_SELECTOR (g_print ("\tStore(%p): store dispatcher: Thread:%p, type:%s, time:%lf\n", ++ selector, g_thread_self (), list_type_string[type], DEBUG_SELECTOR_TIMER_GET);); ++ DEBUG_SELECTOR_TIMER_DESTROY ++ ++ g_task_return_pointer (task, ++ file_items_list, ++ (GDestroyNotify)gedit_open_document_selector_free_file_items_list); ++} ++ ++GList * ++gedit_open_document_selector_store_update_list_finish (GeditOpenDocumentSelectorStore *open_document_selector_store, ++ GAsyncResult *result, ++ GError **error) ++{ ++ g_return_val_if_fail (GEDIT_IS_OPEN_DOCUMENT_SELECTOR_STORE (open_document_selector_store), NULL); ++ g_return_val_if_fail (g_task_is_valid (result, open_document_selector_store), NULL); ++ ++ return g_task_propagate_pointer (G_TASK (result), error); ++} ++ ++void ++gedit_open_document_selector_store_update_list_async (GeditOpenDocumentSelectorStore *selector_store, ++ GeditOpenDocumentSelector *selector, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ ListType type, ++ gpointer user_data) ++{ ++ GTask *task; ++ PushMessage *message; ++ ++ g_return_if_fail (GEDIT_IS_OPEN_DOCUMENT_SELECTOR_STORE (selector_store)); ++ g_return_if_fail (selector == NULL || GEDIT_IS_OPEN_DOCUMENT_SELECTOR (selector)); ++ ++ message = g_new (PushMessage, 1); ++ message->selector = selector; ++ message->type = type; ++ ++ task = g_task_new (selector_store, cancellable, callback, user_data); ++ g_task_set_source_tag (task, gedit_open_document_selector_store_update_list_async); ++ g_task_set_priority (task, G_PRIORITY_DEFAULT); ++ g_task_set_task_data (task, message, (GDestroyNotify)g_free); ++ ++ if (type == GEDIT_OPEN_DOCUMENT_SELECTOR_RECENT_FILES_LIST && ++ selector_store->recent_source == NULL) ++ { ++ selector_store->recent_source = g_idle_source_new (); ++ g_task_attach_source (task, selector_store->recent_source, update_recent_list); ++ } ++ else ++ { ++ g_task_run_in_thread (task, update_list_dispatcher); ++ } ++ ++ g_object_unref (task); ++} ++ ++static void ++gedit_open_document_selector_store_init (GeditOpenDocumentSelectorStore *selector_store) ++{ ++ gedit_recent_configuration_init_default (&selector_store->recent_config); ++ ++ /* We remove the recent files limit since we need the whole list but ++ * we back it up as gedit_open_document_selector_store_get_recent_limit ++ * use it ++ */ ++ selector_store->recent_config_limit = selector_store->recent_config.limit; ++ selector_store->recent_config.limit = -1; ++ ++ g_signal_connect_object (selector_store->recent_config.manager, ++ "changed", ++ G_CALLBACK (on_recent_manager_changed), ++ selector_store, ++ 0); ++ ++ selector_store->recent_items_need_update = TRUE; ++} ++ ++gint ++gedit_open_document_selector_store_get_recent_limit (GeditOpenDocumentSelectorStore *selector_store) ++{ ++ g_return_val_if_fail (GEDIT_IS_OPEN_DOCUMENT_SELECTOR_STORE (selector_store), -1); ++ ++ return selector_store->recent_config_limit; ++} ++ ++void ++gedit_open_document_selector_store_set_filter (GeditOpenDocumentSelectorStore *selector_store, ++ const gchar *filter) ++{ ++ gchar *old_filter; ++ ++ g_return_if_fail (GEDIT_IS_OPEN_DOCUMENT_SELECTOR_STORE (selector_store)); ++ g_return_if_fail (filter != NULL); ++ ++ G_LOCK (recent_files_filter_lock); ++ ++ old_filter = selector_store->filter; ++ selector_store->filter = g_strdup (filter); ++ ++ G_UNLOCK (recent_files_filter_lock); ++ g_free (old_filter); ++} ++ ++gchar * ++gedit_open_document_selector_store_get_filter (GeditOpenDocumentSelectorStore *selector_store) ++{ ++ gchar *recent_filter; ++ ++ g_return_val_if_fail (GEDIT_IS_OPEN_DOCUMENT_SELECTOR_STORE (selector_store), NULL); ++ ++ G_LOCK (recent_files_filter_lock); ++ recent_filter = g_strdup (selector_store->filter); ++ G_UNLOCK (recent_files_filter_lock); ++ ++ return recent_filter; ++} ++ ++/* Gets a unique instance of #GeditOpenDocumentSelectorStore ++ * ++ * Returns: (transfer none): A unique #GeditOpenDocumentSelectorStore. ++ * Do not ref or unref it, it will be destroyed at the end of the application. ++ */ ++GeditOpenDocumentSelectorStore * ++gedit_open_document_selector_store_get_default (void) ++{ ++ static GeditOpenDocumentSelectorStore *instance; ++ ++ if (instance == NULL) ++ { ++ instance = g_object_new (GEDIT_TYPE_OPEN_DOCUMENT_SELECTOR_STORE, NULL); ++ g_object_add_weak_pointer (G_OBJECT (instance), (gpointer) &instance); ++ } ++ ++ return instance; ++} ++ ++/* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-open-document-selector-store.h b/gedit/gedit-open-document-selector-store.h +new file mode 100644 +index 000000000..d1e17a908 +--- /dev/null ++++ b/gedit/gedit-open-document-selector-store.h +@@ -0,0 +1,68 @@ ++/* ++ * gedit-open-document-selector-store.h ++ * This file is part of gedit ++ * ++ * Copyright (C) 2015 - Sébastien Lafargue ++ * ++ * gedit 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. ++ * ++ * gedit 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 gedit. If not, see . ++ */ ++ ++#ifndef GEDIT_OPEN_DOCUMENT_SELECTOR_STORE_H ++#define GEDIT_OPEN_DOCUMENT_SELECTOR_STORE_H ++ ++#include "gedit-open-document-selector-helper.h" ++#include "gedit-open-document-selector.h" ++ ++#include ++#include ++ ++G_BEGIN_DECLS ++ ++#define GEDIT_TYPE_OPEN_DOCUMENT_SELECTOR_STORE (gedit_open_document_selector_store_get_type ()) ++ ++G_DECLARE_FINAL_TYPE (GeditOpenDocumentSelectorStore, gedit_open_document_selector_store, GEDIT, OPEN_DOCUMENT_SELECTOR_STORE, GObject) ++ ++#define GEDIT_OPEN_DOCUMENT_SELECTOR_STORE_ERROR gedit_open_document_selector_store_error_quark () ++ ++typedef enum ++{ ++ TYPE_OUT_OF_RANGE ++} GeditOpenDocumentSelectorStoreError; ++ ++GQuark gedit_open_document_selector_store_error_quark (void); ++ ++gint gedit_open_document_selector_store_get_recent_limit (GeditOpenDocumentSelectorStore *store); ++ ++void gedit_open_document_selector_store_set_filter (GeditOpenDocumentSelectorStore *store, ++ const gchar *filter); ++ ++gchar *gedit_open_document_selector_store_get_filter (GeditOpenDocumentSelectorStore *store); ++ ++GList *gedit_open_document_selector_store_update_list_finish (GeditOpenDocumentSelectorStore *open_document_selector_store, ++ GAsyncResult *res, ++ GError **error); ++ ++void gedit_open_document_selector_store_update_list_async (GeditOpenDocumentSelectorStore *open_document_selector_store, ++ GeditOpenDocumentSelector *open_document_selector, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ ListType type, ++ gpointer user_data); ++ ++GeditOpenDocumentSelectorStore *gedit_open_document_selector_store_get_default (void); ++ ++G_END_DECLS ++ ++#endif /* GEDIT_OPEN_DOCUMENT_SELECTOR_STORE_H */ ++/* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-open-document-selector.c b/gedit/gedit-open-document-selector.c +new file mode 100644 +index 000000000..f67a6ba6d +--- /dev/null ++++ b/gedit/gedit-open-document-selector.c +@@ -0,0 +1,1304 @@ ++/* ++ * gedit-open-document-selector.c ++ * This file is part of gedit ++ * ++ * Copyright (C) 2014 - Sébastien Lafargue ++ * ++ * gedit 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. ++ * ++ * gedit 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 gedit. If not, see . ++ */ ++ ++#include "gedit-open-document-selector.h" ++#include "gedit-open-document-selector-store.h" ++#include "gedit-open-document-selector-helper.h" ++ ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "gedit-recent.h" ++#include "gedit-utils.h" ++#include "gedit-window.h" ++#include "gedit-debug.h" ++ ++struct _GeditOpenDocumentSelector ++{ ++ GtkBox parent_instance; ++ ++ GeditWindow *window; ++ GtkWidget *search_entry; ++ ++ GtkWidget *open_button; ++ GtkWidget *treeview; ++ GtkListStore *liststore; ++ GtkCellRenderer *name_renderer; ++ GtkCellRenderer *path_renderer; ++ GtkWidget *placeholder_box; ++ GtkWidget *scrolled_window; ++ ++ guint populate_listbox_id; ++ ++ GdkRGBA name_label_color; ++ PangoFontDescription *name_font; ++ GdkRGBA path_label_color; ++ PangoFontDescription *path_font; ++ gchar *match_markup_color; ++ ++ GeditOpenDocumentSelectorStore *selector_store; ++ GList *recent_items; ++ GList *home_dir_items; ++ GList *desktop_dir_items; ++ GList *local_bookmarks_dir_items; ++ GList *file_browser_root_items; ++ GList *active_doc_dir_items; ++ GList *current_docs_items; ++ GList *all_items; ++}; ++ ++typedef enum ++{ ++ SELECTOR_TAG_NONE, ++ SELECTOR_TAG_MATCH ++} SelectorTag; ++ ++enum ++{ ++ NAME_COLUMN, ++ PATH_COLUMN, ++ URI_COLUMN, ++ N_COLUMNS ++}; ++ ++enum ++{ ++ PROP_0, ++ PROP_WINDOW, ++ LAST_PROP ++}; ++ ++static GParamSpec *properties[LAST_PROP]; ++ ++enum ++{ ++ SELECTOR_FILE_ACTIVATED, ++ LAST_SIGNAL ++}; ++ ++static guint signals[LAST_SIGNAL]; ++ ++/* Value 0xFF is reserved to mark the end of the array */ ++#define BYTE_ARRAY_END 0xFF ++ ++#define OPEN_DOCUMENT_SELECTOR_WIDTH 400 ++#define OPEN_DOCUMENT_SELECTOR_MAX_VISIBLE_ROWS 10 ++ ++G_DEFINE_TYPE (GeditOpenDocumentSelector, gedit_open_document_selector, GTK_TYPE_BOX) ++ ++static inline const guint8 * ++get_byte_run (const guint8 *byte_array, ++ gsize *count, ++ SelectorTag *tag) ++{ ++ guint8 tag_found; ++ gsize c = 1; ++ ++ tag_found = *byte_array++; ++ ++ while ( *byte_array != BYTE_ARRAY_END && *byte_array == tag_found) ++ { ++ c++; ++ byte_array++; ++ } ++ ++ *count = c; ++ *tag = tag_found; ++ ++ return ( *byte_array != BYTE_ARRAY_END) ? byte_array : NULL; ++} ++ ++static gchar* ++get_markup_from_tagged_byte_array (GeditOpenDocumentSelector *selector, ++ const gchar *str, ++ const guint8 *byte_array) ++{ ++ gchar *txt; ++ GString *string; ++ gchar *result_str; ++ SelectorTag tag; ++ gsize count; ++ ++ string = g_string_sized_new (255); ++ ++ while (TRUE) ++ { ++ byte_array = get_byte_run (byte_array, &count, &tag); ++ txt = g_markup_escape_text (str, count); ++ if (tag == SELECTOR_TAG_MATCH) ++ { ++ g_string_append (string, selector->match_markup_color); ++ g_string_append (string, txt); ++ g_string_append (string, ""); ++ } ++ else ++ { ++ g_string_append (string, txt); ++ } ++ ++ g_free (txt); ++ ++ if (!byte_array) ++ { ++ break; ++ } ++ ++ str = (const gchar *)((gsize)str + count); ++ } ++ ++ result_str = g_string_free (string, FALSE); ++ return result_str; ++} ++ ++static guint8 * ++get_tagged_byte_array (const gchar *uri, ++ GRegex *filter_regex) ++{ ++ guint8 *byte_array; ++ gsize uri_len; ++ GMatchInfo *match_info; ++ gboolean no_match = TRUE; ++ ++ g_return_val_if_fail (uri != NULL, NULL); ++ ++ uri_len = strlen (uri); ++ byte_array = g_malloc0 (uri_len + 1); ++ byte_array[uri_len] = BYTE_ARRAY_END; ++ ++ if (g_regex_match (filter_regex, uri, 0, &match_info) == TRUE) ++ { ++ while (g_match_info_matches (match_info) == TRUE) ++ { ++ guint8 *p; ++ gint match_len; ++ gint start_pos; ++ gint end_pos; ++ ++ if (g_match_info_fetch_pos (match_info, 0, &start_pos, &end_pos) == TRUE) ++ { ++ match_len = end_pos - start_pos; ++ no_match = FALSE; ++ ++ p = (guint8 *)((gsize)byte_array + start_pos); ++ memset (p, SELECTOR_TAG_MATCH, match_len); ++ } ++ ++ g_match_info_next (match_info, NULL); ++ } ++ } ++ ++ g_match_info_free (match_info); ++ ++ if (no_match) ++ { ++ g_free (byte_array); ++ return NULL; ++ } ++ ++ return byte_array; ++} ++ ++static void ++get_markup_for_path_and_name (GeditOpenDocumentSelector *selector, ++ GRegex *filter_regex, ++ const gchar *src_path, ++ const gchar *src_name, ++ gchar **dst_path, ++ gchar **dst_name) ++{ ++ gchar *filename; ++ gsize path_len; ++ gsize name_len; ++ gsize path_separator_len; ++ guint8 *byte_array; ++ guint8 *path_byte_array; ++ guint8 *name_byte_array; ++ ++ filename = g_build_filename (src_path, src_name, NULL); ++ ++ path_len = g_utf8_strlen (src_path, -1); ++ name_len = g_utf8_strlen (src_name, -1); ++ path_separator_len = g_utf8_strlen (filename, -1) - ( path_len + name_len); ++ ++ byte_array = get_tagged_byte_array (filename, filter_regex); ++ if (byte_array) ++ { ++ path_byte_array = g_memdup (byte_array, path_len + 1); ++ path_byte_array[path_len] = BYTE_ARRAY_END; ++ ++ /* name_byte_array is part of byte_array, so released with it */ ++ name_byte_array = (guint8 *)((gsize)byte_array + path_len + path_separator_len); ++ ++ *dst_path = get_markup_from_tagged_byte_array (selector, src_path, path_byte_array); ++ *dst_name = get_markup_from_tagged_byte_array (selector, src_name, name_byte_array); ++ ++ g_free (byte_array); ++ g_free (path_byte_array); ++ } ++ else ++ { ++ *dst_path = g_strdup (src_path); ++ *dst_name = g_strdup (src_name); ++ } ++ ++ g_free (filename); ++} ++ ++static void ++create_row (GeditOpenDocumentSelector *selector, ++ const FileItem *item, ++ GRegex *filter_regex) ++{ ++ GtkTreeIter iter; ++ gchar *uri; ++ gchar *dst_path; ++ gchar *dst_name; ++ ++ uri =item->uri; ++ ++ if (filter_regex) ++ { ++ get_markup_for_path_and_name (selector, ++ filter_regex, ++ (const gchar *)item->path, ++ (const gchar *)item->name, ++ &dst_path, ++ &dst_name); ++ } ++ else ++ { ++ dst_path = g_markup_escape_text (item->path, -1); ++ dst_name = g_markup_escape_text (item->name, -1); ++ } ++ ++ gtk_list_store_append (selector->liststore, &iter); ++ gtk_list_store_set (selector->liststore, &iter, ++ URI_COLUMN, uri, ++ NAME_COLUMN, dst_name, ++ PATH_COLUMN, dst_path, ++ -1); ++ ++ g_free (dst_path); ++ g_free (dst_name); ++} ++ ++static gint ++sort_items_by_mru (FileItem *a, ++ FileItem *b, ++ gpointer unused G_GNUC_UNUSED) ++{ ++ glong diff; ++ ++ g_assert (a != NULL && b != NULL); ++ diff = b->access_time.tv_sec - a->access_time.tv_sec; ++ ++ if (diff == 0) ++ { ++ return (b->access_time.tv_usec - a->access_time.tv_usec); ++ } ++ else ++ { ++ return diff; ++ } ++} ++ ++static GList * ++compute_all_items_list (GeditOpenDocumentSelector *selector) ++{ ++ GList *recent_items; ++ GList *home_dir_items; ++ GList *desktop_dir_items; ++ GList *local_bookmarks_dir_items; ++ GList *file_browser_root_items; ++ GList *active_doc_dir_items; ++ GList *current_docs_items; ++ GList *all_items = NULL; ++ ++ /* Copy/concat the whole list */ ++ recent_items = gedit_open_document_selector_copy_file_items_list ((const GList *)selector->recent_items); ++ home_dir_items = gedit_open_document_selector_copy_file_items_list ((const GList *)selector->home_dir_items); ++ desktop_dir_items = gedit_open_document_selector_copy_file_items_list ((const GList *)selector->desktop_dir_items); ++ local_bookmarks_dir_items = gedit_open_document_selector_copy_file_items_list ((const GList *)selector->local_bookmarks_dir_items); ++ file_browser_root_items = gedit_open_document_selector_copy_file_items_list ((const GList *)selector->file_browser_root_items); ++ active_doc_dir_items = gedit_open_document_selector_copy_file_items_list ((const GList *)selector->active_doc_dir_items); ++ current_docs_items = gedit_open_document_selector_copy_file_items_list ((const GList *)selector->current_docs_items); ++ ++ if (selector->all_items) ++ { ++ gedit_open_document_selector_free_file_items_list (selector->all_items); ++ selector->all_items = NULL; ++ } ++ ++ all_items = g_list_concat (all_items, recent_items); ++ all_items = g_list_concat (all_items, home_dir_items); ++ all_items = g_list_concat (all_items, desktop_dir_items); ++ all_items = g_list_concat (all_items, local_bookmarks_dir_items); ++ all_items = g_list_concat (all_items, file_browser_root_items); ++ all_items = g_list_concat (all_items, active_doc_dir_items); ++ all_items = g_list_concat (all_items, current_docs_items); ++ ++ return all_items; ++} ++ ++static GList * ++clamp_recent_items_list (GList *recent_items, ++ gint limit) ++{ ++ GList *recent_items_capped = NULL; ++ GList *l; ++ FileItem *item; ++ ++ l = recent_items; ++ while (limit > 0 && l != NULL) ++ { ++ item = gedit_open_document_selector_copy_fileitem_item (l->data); ++ recent_items_capped = g_list_prepend (recent_items_capped, item); ++ l = l->next; ++ limit -= 1; ++ } ++ ++ recent_items_capped = g_list_reverse (recent_items_capped); ++ return recent_items_capped; ++} ++ ++/* Setup the fileitem, depending uri's scheme ++ * Return a string to search in. ++ */ ++static gchar * ++fileitem_setup (FileItem *item) ++{ ++ gchar *scheme; ++ gchar *filename; ++ gchar *normalized_filename = NULL; ++ gchar *candidate = NULL; ++ gchar *path; ++ gchar *name; ++ ++ scheme = g_uri_parse_scheme (item->uri); ++ if (g_strcmp0 (scheme, "file") == 0) ++ { ++ filename = g_filename_from_uri ((const gchar *)item->uri, NULL, NULL); ++ if (filename) ++ { ++ path = g_path_get_dirname (filename); ++ item->path = g_filename_to_utf8 (path, -1, NULL, NULL, NULL); ++ g_free (path); ++ ++ name = g_path_get_basename (filename); ++ item->name = g_filename_to_utf8 (name, -1, NULL, NULL, NULL); ++ g_free (name); ++ ++ normalized_filename = g_utf8_normalize (filename, -1, G_NORMALIZE_ALL); ++ g_free (filename); ++ } ++ } ++ else ++ { ++ GFile *file; ++ gchar *parse_name; ++ ++ file = g_file_new_for_uri (item->uri); ++ item->path = gedit_utils_location_get_dirname_for_display (file); ++ item->name = gedit_utils_basename_for_display (file); ++ parse_name = g_file_get_parse_name (file); ++ g_object_unref (file); ++ ++ normalized_filename = g_utf8_normalize (parse_name, -1, G_NORMALIZE_ALL); ++ g_free (parse_name); ++ } ++ ++ if (normalized_filename) ++ { ++ candidate = g_utf8_casefold (normalized_filename, -1); ++ g_free (normalized_filename); ++ } ++ ++ g_free (scheme); ++ ++ return candidate; ++} ++ ++static inline gboolean ++is_filter_in_candidate (const gchar *candidate, ++ const gchar *filter) ++{ ++ gchar *candidate_fold; ++ gboolean ret; ++ ++ g_assert (candidate != NULL); ++ g_assert (filter != NULL); ++ ++ candidate_fold = g_utf8_casefold (candidate, -1); ++ ret = (strstr (candidate_fold, filter) != NULL); ++ ++ g_free (candidate_fold); ++ return ret; ++} ++ ++/* If filter == NULL then items are ++ * not checked against the filter. ++ */ ++static GList * ++fileitem_list_filter (GList *items, ++ const gchar *filter) ++{ ++ GList *new_items = NULL; ++ GList *l; ++ gchar *filter_fold = NULL; ++ ++ if (filter != NULL) ++ filter_fold = g_utf8_casefold (filter, -1); ++ ++ for (l = items; l != NULL; l = l->next) ++ { ++ FileItem *item; ++ gchar *candidate; ++ ++ item = l->data; ++ candidate = fileitem_setup (item); ++ if (candidate != NULL) ++ { ++ if (filter == NULL || is_filter_in_candidate (candidate, filter_fold)) ++ { ++ new_items = g_list_prepend (new_items, ++ gedit_open_document_selector_copy_fileitem_item (item)); ++ } ++ ++ g_free (candidate); ++ } ++ } ++ ++ g_free (filter_fold); ++ new_items = g_list_reverse (new_items); ++ return new_items; ++} ++ ++/* Remove duplicated, the HEAD of the list never change, ++ * the list passed in is modified. ++ */ ++static void ++fileitem_list_remove_duplicates (GList *items) ++{ ++ GList *l; ++ G_GNUC_UNUSED GList *dummy_ptr; ++ ++ l = items; ++ while (l != NULL) ++ { ++ gchar *l_uri, *l1_uri; ++ GList *l1; ++ ++ if ((l1 = l->next) == NULL) ++ { ++ break; ++ } ++ ++ l_uri = ((FileItem *)l->data)->uri; ++ l1_uri = ((FileItem *)l1->data)->uri; ++ if (g_strcmp0 (l_uri, l1_uri) == 0) ++ { ++ gedit_open_document_selector_free_fileitem_item ((FileItem *)l1->data); ++ dummy_ptr = g_list_delete_link (items, l1); ++ } ++ else ++ { ++ l = l->next; ++ } ++ } ++} ++ ++static gboolean ++real_populate_liststore (gpointer data) ++{ ++ GeditOpenDocumentSelector *selector = GEDIT_OPEN_DOCUMENT_SELECTOR (data); ++ GeditOpenDocumentSelectorStore *selector_store; ++ GList *l; ++ GList *filter_items = NULL; ++ gchar *filter; ++ GRegex *filter_regex = NULL; ++ ++ DEBUG_SELECTOR_TIMER_DECL ++ DEBUG_SELECTOR_TIMER_NEW ++ ++ gtk_list_store_clear (selector->liststore); ++ ++ selector_store = selector->selector_store; ++ filter = gedit_open_document_selector_store_get_filter (selector_store); ++ if (filter && *filter != '\0') ++ { ++ DEBUG_SELECTOR (g_print ("Selector(%p): populate liststore: all lists\n", selector);); ++ ++ filter_items = fileitem_list_filter (selector->all_items, (const gchar *)filter); ++ filter_items = g_list_sort_with_data (filter_items, (GCompareDataFunc)sort_items_by_mru, NULL); ++ fileitem_list_remove_duplicates (filter_items); ++ ++ filter_regex = g_regex_new (filter, G_REGEX_CASELESS, 0, NULL); ++ } ++ else ++ { ++ gint recent_limit; ++ GList *recent_items; ++ ++ DEBUG_SELECTOR (g_print ("Selector(%p): populate liststore: recent files list\n", selector);); ++ ++ recent_limit = gedit_open_document_selector_store_get_recent_limit (selector_store); ++ ++ if (recent_limit > 0 ) ++ { ++ recent_items = fileitem_list_filter (selector->recent_items, NULL); ++ filter_items = clamp_recent_items_list (recent_items, recent_limit); ++ gedit_open_document_selector_free_file_items_list (recent_items); ++ } ++ else ++ { ++ filter_items = fileitem_list_filter (selector->recent_items, NULL); ++ } ++ } ++ ++ g_free (filter); ++ ++ DEBUG_SELECTOR (g_print ("Selector(%p): populate liststore: length:%i\n", ++ selector, g_list_length (filter_items));); ++ ++ /* Show the placeholder if no results, show the treeview otherwise */ ++ gtk_widget_set_visible (selector->scrolled_window, (filter_items != NULL)); ++ gtk_widget_set_visible (selector->placeholder_box, (filter_items == NULL)); ++ ++ for (l = filter_items; l != NULL; l = l->next) ++ { ++ FileItem *item; ++ ++ item = l->data; ++ create_row (selector, (const FileItem *)item, filter_regex); ++ } ++ ++ if (filter_regex) ++ { ++ g_regex_unref (filter_regex); ++ } ++ ++ gedit_open_document_selector_free_file_items_list (filter_items); ++ ++ DEBUG_SELECTOR (g_print ("Selector(%p): populate liststore: time:%lf\n\n", ++ selector, DEBUG_SELECTOR_TIMER_GET);); ++ DEBUG_SELECTOR_TIMER_DESTROY ++ ++ selector->populate_listbox_id = 0; ++ return G_SOURCE_REMOVE; ++} ++ ++static void ++populate_liststore (GeditOpenDocumentSelector *selector) ++{ ++ /* Populate requests are compressed */ ++ if (selector->populate_listbox_id != 0) ++ { ++ DEBUG_SELECTOR (g_print ("Selector(%p): populate liststore: idle\n", selector);); ++ return; ++ } ++ ++ DEBUG_SELECTOR (g_print ("Selector(%p): populate liststore: scheduled\n", selector);); ++ selector->populate_listbox_id = gdk_threads_add_idle_full (G_PRIORITY_HIGH_IDLE + 30, ++ real_populate_liststore, ++ selector, ++ NULL); ++} ++ ++static gboolean ++on_treeview_key_press (GtkTreeView *treeview, ++ GdkEventKey *event, ++ GeditOpenDocumentSelector *selector) ++{ ++ guint keyval; ++ gboolean is_control_pressed; ++ GtkTreeSelection *tree_selection; ++ GtkTreePath *root_path; ++ GdkModifierType modifiers; ++ ++ if (gdk_event_get_keyval ((GdkEvent *)event, &keyval) == TRUE) ++ { ++ tree_selection = gtk_tree_view_get_selection (treeview); ++ root_path = gtk_tree_path_new_from_string ("0"); ++ ++ modifiers = gtk_accelerator_get_default_mod_mask (); ++ is_control_pressed = (event->state & modifiers) == GDK_CONTROL_MASK; ++ ++ if ((keyval == GDK_KEY_Up || keyval == GDK_KEY_KP_Up) && ++ !is_control_pressed) ++ { ++ if (gtk_tree_selection_path_is_selected (tree_selection, root_path)) ++ { ++ gtk_tree_selection_unselect_all (tree_selection); ++ gtk_widget_grab_focus (selector->search_entry); ++ ++ return GDK_EVENT_STOP; ++ } ++ } ++ } ++ ++ return GDK_EVENT_PROPAGATE; ++} ++ ++static void ++on_entry_changed (GtkEntry *entry, ++ GeditOpenDocumentSelector *selector) ++{ ++ const gchar *entry_text; ++ ++ entry_text = gtk_entry_get_text (entry); ++ gedit_open_document_selector_store_set_filter (selector->selector_store, ++ entry_text); ++ ++ if (gtk_widget_get_mapped ( GTK_WIDGET (selector))) ++ { ++ populate_liststore (selector); ++ } ++} ++ ++static void ++on_entry_activated (GtkEntry *entry, ++ GeditOpenDocumentSelector *selector) ++{ ++ const gchar *entry_text; ++ GtkTreeSelection *selection; ++ gchar *uri; ++ GFile *file; ++ gchar *scheme; ++ ++ entry_text = gtk_entry_get_text (entry); ++ scheme = g_uri_parse_scheme (entry_text); ++ if (!scheme) ++ { ++ const gchar *home_dir = g_get_home_dir (); ++ ++ if ( home_dir != NULL && g_str_has_prefix (entry_text, "~/")) ++ { ++ uri = g_strconcat ("file://", home_dir, "/", entry_text + 2, NULL); ++ } ++ else ++ { ++ uri = g_strconcat ("file://", entry_text, NULL); ++ } ++ } ++ else ++ { ++ g_free (scheme); ++ uri = g_strdup (entry_text); ++ } ++ ++ file = g_file_new_for_uri (uri); ++ if (g_file_query_exists (file, NULL)) ++ { ++ DEBUG_SELECTOR (g_print ("Selector(%p): search entry activated : loading '%s'\n", ++ selector, uri);); ++ ++ gtk_entry_set_text (entry, ""); ++ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (selector->treeview)); ++ gtk_tree_selection_unselect_all (selection); ++ ++ g_signal_emit (G_OBJECT (selector), signals[SELECTOR_FILE_ACTIVATED], 0, uri); ++ } ++ ++ g_object_unref (file); ++} ++ ++static void ++gedit_open_document_selector_dispose (GObject *object) ++{ ++ GeditOpenDocumentSelector *selector = GEDIT_OPEN_DOCUMENT_SELECTOR (object); ++ ++ if (selector->populate_listbox_id != 0) ++ { ++ g_source_remove (selector->populate_listbox_id); ++ selector->populate_listbox_id = 0; ++ } ++ ++ g_clear_pointer (&selector->name_font, pango_font_description_free); ++ g_clear_pointer (&selector->path_font, pango_font_description_free); ++ g_clear_pointer (&selector->match_markup_color, g_free); ++ ++ if (selector->recent_items) ++ { ++ gedit_open_document_selector_free_file_items_list (selector->recent_items); ++ selector->recent_items = NULL; ++ } ++ ++ if (selector->home_dir_items) ++ { ++ gedit_open_document_selector_free_file_items_list (selector->home_dir_items); ++ selector->home_dir_items = NULL; ++ } ++ ++ if (selector->desktop_dir_items) ++ { ++ gedit_open_document_selector_free_file_items_list (selector->desktop_dir_items); ++ selector->desktop_dir_items = NULL; ++ } ++ ++ if (selector->local_bookmarks_dir_items) ++ { ++ gedit_open_document_selector_free_file_items_list (selector->local_bookmarks_dir_items); ++ selector->local_bookmarks_dir_items = NULL; ++ } ++ ++ if (selector->file_browser_root_items) ++ { ++ gedit_open_document_selector_free_file_items_list (selector->file_browser_root_items); ++ selector->file_browser_root_items = NULL; ++ } ++ ++ if (selector->active_doc_dir_items) ++ { ++ gedit_open_document_selector_free_file_items_list (selector->active_doc_dir_items); ++ selector->active_doc_dir_items = NULL; ++ } ++ ++ if (selector->current_docs_items) ++ { ++ gedit_open_document_selector_free_file_items_list (selector->current_docs_items); ++ selector->current_docs_items = NULL; ++ } ++ ++ if (selector->all_items) ++ { ++ gedit_open_document_selector_free_file_items_list (selector->all_items); ++ selector->all_items = NULL; ++ } ++ ++ G_OBJECT_CLASS (gedit_open_document_selector_parent_class)->dispose (object); ++} ++ ++static void ++on_row_activated (GtkTreeView *treeview, ++ GtkTreePath *path, ++ GtkTreeViewColumn *column G_GNUC_UNUSED, ++ GeditOpenDocumentSelector *selector) ++{ ++ GtkTreeModel *liststore = GTK_TREE_MODEL (selector->liststore); ++ GtkTreeSelection *selection; ++ GtkTreeIter iter; ++ gchar *uri; ++ ++ g_return_if_fail (gtk_tree_model_get_iter (liststore, &iter, path)); ++ gtk_tree_model_get (liststore, &iter, ++ URI_COLUMN, &uri, ++ -1); ++ ++ selection = gtk_tree_view_get_selection (treeview); ++ gtk_tree_selection_unselect_all (selection); ++ ++ /* Leak of uri */ ++ g_signal_emit (G_OBJECT (selector), signals[SELECTOR_FILE_ACTIVATED], 0, uri); ++} ++ ++static void ++update_list_cb (GeditOpenDocumentSelectorStore *selector_store, ++ GAsyncResult *res, ++ gpointer user_data G_GNUC_UNUSED) ++{ ++ GList *list; ++ GError *error; ++ PushMessage *message; ++ ListType type; ++ GeditOpenDocumentSelector *selector; ++ ++ list = gedit_open_document_selector_store_update_list_finish (selector_store, res, &error); ++ message = g_task_get_task_data (G_TASK (res)); ++ selector = message->selector; ++ type = message->type; ++ ++ DEBUG_SELECTOR (g_print ("Selector(%p): update_list_cb - type:%s, length:%i\n", ++ selector, list_type_string[type], g_list_length (list));); ++ ++ switch (type) ++ { ++ case GEDIT_OPEN_DOCUMENT_SELECTOR_RECENT_FILES_LIST: ++ gedit_open_document_selector_free_file_items_list (selector->recent_items); ++ selector->recent_items = list; ++ break; ++ ++ case GEDIT_OPEN_DOCUMENT_SELECTOR_HOME_DIR_LIST: ++ gedit_open_document_selector_free_file_items_list (selector->home_dir_items); ++ selector->home_dir_items = list; ++ break; ++ ++ case GEDIT_OPEN_DOCUMENT_SELECTOR_DESKTOP_DIR_LIST: ++ gedit_open_document_selector_free_file_items_list (selector->desktop_dir_items); ++ selector->desktop_dir_items = list; ++ break; ++ ++ case GEDIT_OPEN_DOCUMENT_SELECTOR_LOCAL_BOOKMARKS_DIR_LIST: ++ gedit_open_document_selector_free_file_items_list (selector->local_bookmarks_dir_items); ++ selector->local_bookmarks_dir_items = list; ++ break; ++ ++ case GEDIT_OPEN_DOCUMENT_SELECTOR_FILE_BROWSER_ROOT_DIR_LIST: ++ gedit_open_document_selector_free_file_items_list (selector->file_browser_root_items); ++ selector->file_browser_root_items = list; ++ break; ++ ++ case GEDIT_OPEN_DOCUMENT_SELECTOR_ACTIVE_DOC_DIR_LIST: ++ gedit_open_document_selector_free_file_items_list (selector->active_doc_dir_items); ++ selector->active_doc_dir_items = list; ++ break; ++ ++ case GEDIT_OPEN_DOCUMENT_SELECTOR_CURRENT_DOCS_LIST: ++ gedit_open_document_selector_free_file_items_list (selector->current_docs_items); ++ selector->current_docs_items = list; ++ break; ++ ++ default: ++ g_return_if_reached (); ++ } ++ ++ selector->all_items = compute_all_items_list (selector); ++ populate_liststore (selector); ++} ++ ++static void ++gedit_open_document_selector_constructed (GObject *object) ++{ ++ GeditOpenDocumentSelector *selector = GEDIT_OPEN_DOCUMENT_SELECTOR (object); ++ ++ G_OBJECT_CLASS (gedit_open_document_selector_parent_class)->constructed (object); ++ ++ DEBUG_SELECTOR (g_print ("Selector(%p): constructed - ask recent file list\n", selector);); ++ ++ gedit_open_document_selector_store_update_list_async (selector->selector_store, ++ selector, ++ NULL, ++ (GAsyncReadyCallback)update_list_cb, ++ GEDIT_OPEN_DOCUMENT_SELECTOR_RECENT_FILES_LIST, ++ selector); ++} ++ ++static void ++gedit_open_document_selector_mapped (GtkWidget *widget) ++{ ++ GeditOpenDocumentSelector *selector = GEDIT_OPEN_DOCUMENT_SELECTOR (widget); ++ ListType list_number; ++ ++ /* We update all the lists */ ++ DEBUG_SELECTOR (g_print ("Selector(%p): mapped - ask all lists\n", selector);); ++ ++ for (list_number = 0; list_number < GEDIT_OPEN_DOCUMENT_SELECTOR_LIST_TYPE_NUM_OF_LISTS; list_number++) ++ { ++ gedit_open_document_selector_store_update_list_async (selector->selector_store, ++ selector, ++ NULL, ++ (GAsyncReadyCallback)update_list_cb, ++ list_number, ++ selector); ++ } ++ ++ GTK_WIDGET_CLASS (gedit_open_document_selector_parent_class)->map (widget); ++} ++ ++static GtkSizeRequestMode ++gedit_open_document_selector_get_request_mode (GtkWidget *widget G_GNUC_UNUSED) ++{ ++ return GTK_SIZE_REQUEST_CONSTANT_SIZE; ++} ++ ++static void ++gedit_open_document_selector_get_preferred_width (GtkWidget *widget G_GNUC_UNUSED, ++ gint *minimum_width, ++ gint *natural_width) ++{ ++ *minimum_width = *natural_width = OPEN_DOCUMENT_SELECTOR_WIDTH; ++} ++ ++static void ++gedit_open_document_selector_set_property (GObject *object, ++ guint prop_id, ++ const GValue *value, ++ GParamSpec *pspec) ++{ ++ GeditOpenDocumentSelector *selector = GEDIT_OPEN_DOCUMENT_SELECTOR (object); ++ ++ switch (prop_id) ++ { ++ case PROP_WINDOW: ++ selector->window = g_value_get_object (value); ++ break; ++ ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ ++static void ++gedit_open_document_selector_get_property (GObject *object, ++ guint prop_id, ++ GValue *value, ++ GParamSpec *pspec) ++{ ++ GeditOpenDocumentSelector *selector = GEDIT_OPEN_DOCUMENT_SELECTOR (object); ++ ++ switch (prop_id) ++ { ++ case PROP_WINDOW: ++ g_value_set_object (value, selector->window); ++ break; ++ ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ ++static void ++gedit_open_document_selector_file_activated (GeditOpenDocumentSelector *selector G_GNUC_UNUSED, ++ const gchar *uri G_GNUC_UNUSED) ++{ ++ /* Do nothing in the default handler */ ++} ++ ++static void ++gedit_open_document_selector_class_init (GeditOpenDocumentSelectorClass *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); ++ ++ object_class->constructed = gedit_open_document_selector_constructed; ++ object_class->dispose = gedit_open_document_selector_dispose; ++ ++ object_class->get_property = gedit_open_document_selector_get_property; ++ object_class->set_property = gedit_open_document_selector_set_property; ++ ++ widget_class->get_request_mode = gedit_open_document_selector_get_request_mode; ++ widget_class->get_preferred_width = gedit_open_document_selector_get_preferred_width; ++ widget_class->map = gedit_open_document_selector_mapped; ++ ++ properties[PROP_WINDOW] = ++ g_param_spec_object ("window", ++ "Window", ++ "The GeditWindow this GeditOpenDocumentSelector is associated with", ++ GEDIT_TYPE_WINDOW, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); ++ ++ g_object_class_install_properties (object_class, LAST_PROP, properties); ++ ++ signals[SELECTOR_FILE_ACTIVATED] = ++ g_signal_new_class_handler ("file-activated", ++ G_TYPE_FROM_CLASS (klass), ++ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, ++ G_CALLBACK (gedit_open_document_selector_file_activated), ++ NULL, NULL, NULL, ++ G_TYPE_NONE, ++ 1, ++ G_TYPE_STRING); ++ ++ gtk_widget_class_set_template_from_resource (widget_class, ++ "/org/gnome/gedit/ui/gedit-open-document-selector.ui"); ++ ++ gtk_widget_class_bind_template_child (widget_class, GeditOpenDocumentSelector, open_button); ++ gtk_widget_class_bind_template_child (widget_class, GeditOpenDocumentSelector, treeview); ++ gtk_widget_class_bind_template_child (widget_class, GeditOpenDocumentSelector, placeholder_box); ++ gtk_widget_class_bind_template_child (widget_class, GeditOpenDocumentSelector, scrolled_window); ++ gtk_widget_class_bind_template_child (widget_class, GeditOpenDocumentSelector, search_entry); ++} ++ ++static void ++on_treeview_allocate (GtkWidget *widget G_GNUC_UNUSED, ++ GdkRectangle *allocation G_GNUC_UNUSED, ++ GeditOpenDocumentSelector *selector) ++{ ++ GeditOpenDocumentSelectorStore *selector_store; ++ GtkStyleContext *context; ++ gint name_renderer_natural_size; ++ gint path_renderer_natural_size; ++ GtkBorder padding; ++ gint ypad; ++ gint limit_capped; ++ gint treeview_height; ++ gint grid_line_width; ++ gint row_height; ++ gint recent_limit; ++ ++ selector_store = selector->selector_store; ++ ++ context = gtk_widget_get_style_context (selector->treeview); ++ gtk_style_context_get_padding (context, ++ gtk_style_context_get_state (context), ++ &padding); ++ ++ /* Treeview height computation */ ++ gtk_cell_renderer_get_preferred_height (selector->name_renderer, ++ selector->treeview, ++ NULL, ++ &name_renderer_natural_size); ++ ++ gtk_cell_renderer_get_preferred_height (selector->path_renderer, ++ selector->treeview, ++ NULL, ++ &path_renderer_natural_size); ++ ++ gtk_cell_renderer_get_padding (selector->name_renderer, NULL, &ypad); ++ gtk_widget_style_get (selector->treeview, "grid-line-width", &grid_line_width, NULL); ++ ++ recent_limit = gedit_open_document_selector_store_get_recent_limit (selector_store); ++ ++ limit_capped = (recent_limit > 0 ) ? MIN (recent_limit, OPEN_DOCUMENT_SELECTOR_MAX_VISIBLE_ROWS) : ++ OPEN_DOCUMENT_SELECTOR_MAX_VISIBLE_ROWS; ++ ++ row_height = name_renderer_natural_size + ++ path_renderer_natural_size + ++ 2 * (padding.top + padding.bottom) + ++ ypad + ++ grid_line_width; ++ ++ treeview_height = row_height * limit_capped; ++ gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (selector->scrolled_window), ++ treeview_height); ++ gtk_scrolled_window_set_max_content_height (GTK_SCROLLED_WINDOW (selector->scrolled_window), ++ treeview_height); ++ ++ gtk_widget_set_size_request (selector->placeholder_box, -1, treeview_height); ++} ++ ++static inline gchar * ++rgba_to_hex8 (GdkRGBA *rgba) ++{ ++ guint red = (guint)(0.5 + CLAMP (rgba->red, 0.0, 1.0) * 255.0); ++ guint green = (guint)(0.5 + CLAMP (rgba->green, 0.0, 1.0) * 255.0); ++ guint blue = (guint)(0.5 + CLAMP (rgba->blue, 0.0, 1.0) * 255.0); ++ guint alpha = (guint)(0.5 + CLAMP (rgba->alpha, 0.0, 1.0) * 255.0); ++ gchar *str = g_strdup_printf ("#%02X%02X%02X%02X", red, green, blue, alpha); ++ ++ return str; ++} ++ ++static void ++on_treeview_style_updated (GtkWidget *widget, ++ GeditOpenDocumentSelector *selector) ++{ ++ GtkStyleContext *context; ++ GdkRGBA match_foreground_rgba = {0.0, 0.0, 0.0, 0.0}; ++ GdkRGBA match_background_rgba = {0.0, 0.0, 0.0, 0.0}; ++ gchar *match_foreground_hex8; ++ gchar *match_background_hex8; ++ ++ context = gtk_widget_get_style_context (widget); ++ ++ /* Name label foreground and font size styling */ ++ gtk_style_context_save (context); ++ gtk_style_context_add_class (context, "open-document-selector-name-label"); ++ ++ gtk_style_context_get_color (context, ++ gtk_style_context_get_state (context), ++ &selector->name_label_color); ++ ++ g_clear_pointer (&selector->name_font, pango_font_description_free); ++ gtk_style_context_get (context, ++ gtk_style_context_get_state (context), ++ "font", &selector->name_font, ++ NULL); ++ ++ gtk_style_context_restore (context); ++ ++ /* Path label foreground and font size styling */ ++ gtk_style_context_save (context); ++ gtk_style_context_add_class (context, "open-document-selector-path-label"); ++ ++ gtk_style_context_get_color (context, ++ gtk_style_context_get_state (context), ++ &selector->path_label_color); ++ ++ g_clear_pointer (&selector->path_font, pango_font_description_free); ++ gtk_style_context_get (context, ++ gtk_style_context_get_state (context), ++ "font", &selector->path_font, ++ NULL); ++ ++ gtk_style_context_restore (context); ++ ++ /* Match styling */ ++ gtk_style_context_save (context); ++ gtk_style_context_add_class (context, "open-document-selector-match"); ++ ++ gtk_style_context_get_color (context, ++ gtk_style_context_get_state (context), ++ &match_foreground_rgba); ++ ++ G_GNUC_BEGIN_IGNORE_DEPRECATIONS; ++ gtk_style_context_get_background_color (context, ++ gtk_style_context_get_state (context), ++ &match_background_rgba); ++ G_GNUC_END_IGNORE_DEPRECATIONS; ++ ++ gtk_style_context_restore (context); ++ g_free (selector->match_markup_color); ++ ++ match_foreground_hex8 = rgba_to_hex8 (&match_foreground_rgba); ++ match_background_hex8 = rgba_to_hex8 (&match_background_rgba); ++ ++ selector->match_markup_color = g_strdup_printf ("", ++ match_foreground_hex8, ++ match_background_hex8); ++ ++ g_free (match_foreground_hex8); ++ g_free (match_background_hex8); ++} ++ ++static void ++name_renderer_datafunc (GtkTreeViewColumn *column G_GNUC_UNUSED, ++ GtkCellRenderer *name_renderer G_GNUC_UNUSED, ++ GtkTreeModel *liststore G_GNUC_UNUSED, ++ GtkTreeIter *iter G_GNUC_UNUSED, ++ GeditOpenDocumentSelector *selector) ++{ ++ g_object_set (selector->name_renderer, "foreground-rgba", &selector->name_label_color, NULL); ++ g_object_set (selector->name_renderer, "font-desc", selector->name_font, NULL); ++} ++ ++static void ++path_renderer_datafunc (GtkTreeViewColumn *column G_GNUC_UNUSED, ++ GtkCellRenderer *path_renderer G_GNUC_UNUSED, ++ GtkTreeModel *liststore G_GNUC_UNUSED, ++ GtkTreeIter *iter G_GNUC_UNUSED, ++ GeditOpenDocumentSelector *selector) ++{ ++ g_object_set (selector->path_renderer, "foreground-rgba", &selector->path_label_color, NULL); ++ g_object_set (selector->path_renderer, "font-desc", selector->path_font, NULL); ++} ++ ++static void ++setup_treeview (GeditOpenDocumentSelector *selector) ++{ ++ GtkTreeViewColumn *column; ++ GtkCellArea *cell_area; ++ GtkStyleContext *context; ++ ++ gtk_tree_view_set_model (GTK_TREE_VIEW (selector->treeview), GTK_TREE_MODEL (selector->liststore)); ++ g_object_unref(GTK_TREE_MODEL (selector->liststore)); ++ ++ selector->name_renderer = gtk_cell_renderer_text_new (); ++ selector->path_renderer = gtk_cell_renderer_text_new (); ++ ++ g_object_set (selector->name_renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL); ++ g_object_set (selector->path_renderer, "ellipsize", PANGO_ELLIPSIZE_START, NULL); ++ ++ column = gtk_tree_view_column_new (); ++ gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); ++ ++ gtk_tree_view_column_pack_start (column, selector->name_renderer, TRUE); ++ gtk_tree_view_column_pack_start (column, selector->path_renderer, TRUE); ++ ++ gtk_tree_view_column_set_attributes (column, selector->name_renderer, "markup", NAME_COLUMN, NULL); ++ gtk_tree_view_column_set_attributes (column, selector->path_renderer, "markup", PATH_COLUMN, NULL); ++ ++ gtk_tree_view_append_column (GTK_TREE_VIEW (selector->treeview), column); ++ cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column)); ++ gtk_orientable_set_orientation (GTK_ORIENTABLE (cell_area), GTK_ORIENTATION_VERTICAL); ++ ++ context = gtk_widget_get_style_context (selector->treeview); ++ gtk_style_context_add_class (context, "open-document-selector-treeview"); ++ ++ gtk_tree_view_column_set_cell_data_func (column, ++ selector->name_renderer, ++ (GtkTreeCellDataFunc)name_renderer_datafunc, ++ selector, ++ NULL); ++ ++ gtk_tree_view_column_set_cell_data_func (column, ++ selector->path_renderer, ++ (GtkTreeCellDataFunc)path_renderer_datafunc, ++ selector, ++ NULL); ++} ++ ++static void ++gedit_open_document_selector_init (GeditOpenDocumentSelector *selector) ++{ ++ gedit_debug (DEBUG_WINDOW); ++ ++ gtk_widget_init_template (GTK_WIDGET (selector)); ++ ++ selector->selector_store = gedit_open_document_selector_store_get_default (); ++ ++ selector->liststore = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); ++ setup_treeview (selector); ++ ++ g_signal_connect (selector->search_entry, ++ "changed", ++ G_CALLBACK (on_entry_changed), ++ selector); ++ ++ g_signal_connect (selector->search_entry, ++ "activate", ++ G_CALLBACK (on_entry_activated), ++ selector); ++ ++ g_signal_connect (selector->treeview, ++ "row-activated", ++ G_CALLBACK (on_row_activated), ++ selector); ++ ++ g_signal_connect (selector->treeview, ++ "size-allocate", ++ G_CALLBACK (on_treeview_allocate), ++ selector); ++ ++ g_signal_connect (selector->treeview, ++ "key-press-event", ++ G_CALLBACK (on_treeview_key_press), ++ selector); ++ ++ g_signal_connect (selector->treeview, ++ "style-updated", ++ G_CALLBACK (on_treeview_style_updated), ++ selector); ++} ++ ++GeditOpenDocumentSelector * ++gedit_open_document_selector_new (GeditWindow *window) ++{ ++ g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); ++ ++ return g_object_new (GEDIT_TYPE_OPEN_DOCUMENT_SELECTOR, ++ "window", window, ++ NULL); ++} ++ ++GeditWindow * ++gedit_open_document_selector_get_window (GeditOpenDocumentSelector *selector) ++{ ++ g_return_val_if_fail (GEDIT_IS_OPEN_DOCUMENT_SELECTOR (selector), NULL); ++ ++ return selector->window; ++} ++ ++GtkWidget * ++gedit_open_document_selector_get_search_entry (GeditOpenDocumentSelector *selector) ++{ ++ g_return_val_if_fail (GEDIT_IS_OPEN_DOCUMENT_SELECTOR (selector), NULL); ++ ++ return selector->search_entry; ++} ++ ++/* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-open-document-selector.h b/gedit/gedit-open-document-selector.h +new file mode 100644 +index 000000000..b4d50cefd +--- /dev/null ++++ b/gedit/gedit-open-document-selector.h +@@ -0,0 +1,44 @@ ++/* ++ * gedit-open-document-selector.h ++ * This file is part of gedit ++ * ++ * Copyright (C) 2014 - Sébastien Lafargue ++ * ++ * gedit 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. ++ * ++ * gedit 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 gedit. If not, see . ++ */ ++ ++#ifndef GEDIT_OPEN_DOCUMENT_SELECTOR_H ++#define GEDIT_OPEN_DOCUMENT_SELECTOR_H ++ ++#include ++#include "gedit-window.h" ++ ++#include ++ ++G_BEGIN_DECLS ++ ++#define GEDIT_TYPE_OPEN_DOCUMENT_SELECTOR (gedit_open_document_selector_get_type ()) ++ ++G_DECLARE_FINAL_TYPE (GeditOpenDocumentSelector, gedit_open_document_selector, GEDIT, OPEN_DOCUMENT_SELECTOR, GtkBox) ++ ++GeditOpenDocumentSelector *gedit_open_document_selector_new (GeditWindow *window); ++ ++GeditWindow *gedit_open_document_selector_get_window (GeditOpenDocumentSelector *selector); ++ ++GtkWidget *gedit_open_document_selector_get_search_entry (GeditOpenDocumentSelector *selector); ++ ++G_END_DECLS ++ ++#endif /* GEDIT_OPEN_DOCUMENT_SELECTOR_H */ ++/* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-pango.c b/gedit/gedit-pango.c +new file mode 100644 +index 000000000..0488bbcd2 +--- /dev/null ++++ b/gedit/gedit-pango.c +@@ -0,0 +1,230 @@ ++/* gedit-pango.c ++ * ++ * This file is a copy of pango_font_description_to_css from gtk gtkfontbutton.c ++ * ++ * Copyright (C) 2016 Matthias Clasen ++ * ++ * 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, see . ++ */ ++ ++#define G_LOG_DOMAIN "gedit-pango" ++ ++#include "config.h" ++ ++#include "gedit-pango.h" ++ ++#if PANGO_VERSION_CHECK (1, 44, 0) ++static void ++add_css_variations (GString *s, ++ const char *variations) ++{ ++ const char *p; ++ const char *sep = ""; ++ ++ if (variations == NULL || variations[0] == '\0') ++ { ++ g_string_append (s, "normal"); ++ return; ++ } ++ ++ p = variations; ++ while (p && *p) ++ { ++ const char *start; ++ const char *end, *end2; ++ double value; ++ char name[5]; ++ ++ while (g_ascii_isspace (*p)) p++; ++ ++ start = p; ++ end = strchr (p, ','); ++ if (end && (end - p < 6)) ++ goto skip; ++ ++ name[0] = p[0]; ++ name[1] = p[1]; ++ name[2] = p[2]; ++ name[3] = p[3]; ++ name[4] = '\0'; ++ ++ p += 4; ++ while (g_ascii_isspace (*p)) p++; ++ if (*p == '=') p++; ++ ++ if (p - start < 5) ++ goto skip; ++ ++ value = g_ascii_strtod (p, (char **) &end2); ++ ++ while (end2 && g_ascii_isspace (*end2)) end2++; ++ ++ if (end2 && (*end2 != ',' && *end2 != '\0')) ++ goto skip; ++ ++ g_string_append_printf (s, "%s\"%s\" %g", sep, name, value); ++ sep = ", "; ++ ++skip: ++ p = end ? end + 1 : NULL; ++ } ++} ++#endif ++ ++/** ++ * gedit_pango_font_description_to_css: ++ * ++ * This function will generate CSS suitable for Gtk's CSS engine ++ * based on the properties of the #PangoFontDescription. ++ * ++ * Returns: (transfer full): A newly allocated string containing the ++ * CSS describing the font description. ++ */ ++gchar * ++gedit_pango_font_description_to_css (const PangoFontDescription *desc) ++{ ++ GString *s; ++ PangoFontMask set; ++ ++ s = g_string_new (""); ++ ++ set = pango_font_description_get_set_fields (desc); ++ if (set & PANGO_FONT_MASK_FAMILY) ++ { ++ g_string_append (s, "font-family: "); ++ g_string_append (s, pango_font_description_get_family (desc)); ++ g_string_append (s, "; "); ++ } ++ if (set & PANGO_FONT_MASK_STYLE) ++ { ++ switch (pango_font_description_get_style (desc)) ++ { ++ case PANGO_STYLE_NORMAL: ++ g_string_append (s, "font-style: normal; "); ++ break; ++ case PANGO_STYLE_OBLIQUE: ++ g_string_append (s, "font-style: oblique; "); ++ break; ++ case PANGO_STYLE_ITALIC: ++ g_string_append (s, "font-style: italic; "); ++ break; ++ default: ++ break; ++ } ++ } ++ if (set & PANGO_FONT_MASK_VARIANT) ++ { ++ switch (pango_font_description_get_variant (desc)) ++ { ++ case PANGO_VARIANT_NORMAL: ++ g_string_append (s, "font-variant: normal; "); ++ break; ++ case PANGO_VARIANT_SMALL_CAPS: ++ g_string_append (s, "font-variant: small-caps; "); ++ break; ++ default: ++ break; ++ } ++ } ++ if (set & PANGO_FONT_MASK_WEIGHT) ++ { ++ switch (pango_font_description_get_weight (desc)) ++ { ++ case PANGO_WEIGHT_THIN: ++ g_string_append (s, "font-weight: 100; "); ++ break; ++ case PANGO_WEIGHT_ULTRALIGHT: ++ g_string_append (s, "font-weight: 200; "); ++ break; ++ case PANGO_WEIGHT_LIGHT: ++ case PANGO_WEIGHT_SEMILIGHT: ++ g_string_append (s, "font-weight: 300; "); ++ break; ++ case PANGO_WEIGHT_BOOK: ++ case PANGO_WEIGHT_NORMAL: ++ g_string_append (s, "font-weight: 400; "); ++ break; ++ case PANGO_WEIGHT_MEDIUM: ++ g_string_append (s, "font-weight: 500; "); ++ break; ++ case PANGO_WEIGHT_SEMIBOLD: ++ g_string_append (s, "font-weight: 600; "); ++ break; ++ case PANGO_WEIGHT_BOLD: ++ g_string_append (s, "font-weight: 700; "); ++ break; ++ case PANGO_WEIGHT_ULTRABOLD: ++ g_string_append (s, "font-weight: 800; "); ++ break; ++ case PANGO_WEIGHT_HEAVY: ++ case PANGO_WEIGHT_ULTRAHEAVY: ++ g_string_append (s, "font-weight: 900; "); ++ break; ++ default: ++ break; ++ } ++ } ++ if (set & PANGO_FONT_MASK_STRETCH) ++ { ++ switch (pango_font_description_get_stretch (desc)) ++ { ++ case PANGO_STRETCH_ULTRA_CONDENSED: ++ g_string_append (s, "font-stretch: ultra-condensed; "); ++ break; ++ case PANGO_STRETCH_EXTRA_CONDENSED: ++ g_string_append (s, "font-stretch: extra-condensed; "); ++ break; ++ case PANGO_STRETCH_CONDENSED: ++ g_string_append (s, "font-stretch: condensed; "); ++ break; ++ case PANGO_STRETCH_SEMI_CONDENSED: ++ g_string_append (s, "font-stretch: semi-condensed; "); ++ break; ++ case PANGO_STRETCH_NORMAL: ++ g_string_append (s, "font-stretch: normal; "); ++ break; ++ case PANGO_STRETCH_SEMI_EXPANDED: ++ g_string_append (s, "font-stretch: semi-expanded; "); ++ break; ++ case PANGO_STRETCH_EXPANDED: ++ g_string_append (s, "font-stretch: expanded; "); ++ break; ++ case PANGO_STRETCH_EXTRA_EXPANDED: ++ break; ++ case PANGO_STRETCH_ULTRA_EXPANDED: ++ g_string_append (s, "font-stretch: ultra-expanded; "); ++ break; ++ default: ++ break; ++ } ++ } ++ if (set & PANGO_FONT_MASK_SIZE) ++ { ++ g_string_append_printf (s, "font-size: %dpt; ", pango_font_description_get_size (desc) / PANGO_SCALE); ++ } ++ ++#if PANGO_VERSION_CHECK (1, 44, 0) ++ if (set & PANGO_FONT_MASK_VARIATIONS) ++ { ++ const char *variations; ++ ++ g_string_append (s, "font-variation-settings: "); ++ variations = pango_font_description_get_variations (desc); ++ add_css_variations (s, variations); ++ g_string_append (s, "; "); ++ } ++#endif ++ ++ return g_string_free (s, FALSE); ++} +diff --git a/gedit/gedit-pango.h b/gedit/gedit-pango.h +new file mode 100644 +index 000000000..8c800d502 +--- /dev/null ++++ b/gedit/gedit-pango.h +@@ -0,0 +1,28 @@ ++/* gedit-pango.h ++ * ++ * 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, see . ++ */ ++ ++#ifndef GEDIT_PANGO_H ++#define GEDIT_PANGO_H ++ ++#include ++ ++G_BEGIN_DECLS ++ ++gchar *gedit_pango_font_description_to_css (const PangoFontDescription *font_desc); ++ ++G_END_DECLS ++ ++#endif /* GEDIT_PANGO_H */ +diff --git a/gedit/gedit-preferences-dialog.c b/gedit/gedit-preferences-dialog.c +index 016baf189..ec3f53a3d 100644 +--- a/gedit/gedit-preferences-dialog.c ++++ b/gedit/gedit-preferences-dialog.c +@@ -1,137 +1,148 @@ + /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + /* + * gedit-preferences-dialog.c + * This file is part of gedit + * + * Copyright (C) 2001-2005 Paolo Maggi + * + * 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, see . + */ + + #include "config.h" + + #include "gedit-preferences-dialog.h" + ++#include ++#include ++#include ++ + #include + #include +-#include ++#include + #include + ++#include "gedit-utils.h" + #include "gedit-debug.h" ++#include "gedit-document.h" + #include "gedit-dirs.h" + #include "gedit-settings.h" ++#include "gedit-utils.h" ++#include "gedit-file-chooser-dialog.h" + + /* + * gedit-preferences dialog is a singleton since we don't + * want two dialogs showing an inconsistent state of the + * preferences. + * When gedit_show_preferences_dialog is called and there + * is already a prefs dialog dialog open, it is reparented + * and shown. + */ + + static GtkWidget *preferences_dialog = NULL; + + #define GEDIT_SCHEME_ROW_ID_KEY "gedit-scheme-row-id" + + #define GEDIT_TYPE_PREFERENCES_DIALOG (gedit_preferences_dialog_get_type()) + + G_DECLARE_FINAL_TYPE (GeditPreferencesDialog, gedit_preferences_dialog, GEDIT, PREFERENCES_DIALOG, GtkWindow) + + enum + { + ID_COLUMN = 0, + NAME_COLUMN, + DESC_COLUMN, + NUM_COLUMNS + }; + + enum + { + CLOSE, + LAST_SIGNAL + }; + + static guint signals[LAST_SIGNAL]; + + struct _GeditPreferencesDialog + { + GtkWindow parent_instance; + + GSettings *editor; + GSettings *uisettings; /* unfortunately our settings are split for historical reasons */ + + GtkWidget *notebook; + + /* Font */ + GtkWidget *default_font_checkbutton; + GtkWidget *font_button; + GtkWidget *font_grid; + + /* Style Scheme */ + GtkWidget *schemes_list; + GtkWidget *install_scheme_button; + GtkWidget *uninstall_scheme_button; ++ GtkWidget *schemes_scrolled_window; + GtkWidget *schemes_toolbar; +- GtkFileChooserNative * +- install_scheme_file_chooser; ++ ++ GeditFileChooserDialog * ++ install_scheme_file_schooser; + + /* Tabs */ + GtkWidget *tabs_width_spinbutton; + GtkWidget *insert_spaces_checkbutton; + + /* Auto indentation */ + GtkWidget *auto_indent_checkbutton; + + /* Text Wrapping */ + GtkWidget *wrap_text_checkbutton; + GtkWidget *split_checkbutton; + + /* File Saving */ + GtkWidget *backup_copy_checkbutton; + GtkWidget *auto_save_checkbutton; + GtkWidget *auto_save_spinbutton; + + GtkWidget *display_line_numbers_checkbutton; + GtkWidget *display_statusbar_checkbutton; ++ GtkWidget *display_overview_map_checkbutton; + GtkWidget *display_grid_checkbutton; + + /* Right margin */ + GtkWidget *right_margin_checkbutton; + GtkWidget *right_margin_position_grid; + GtkWidget *right_margin_position_spinbutton; + + /* Highlighting */ + GtkWidget *highlight_current_line_checkbutton; + GtkWidget *bracket_matching_checkbutton; + + /* Plugin manager */ + GtkWidget *plugin_manager; + }; + + G_DEFINE_TYPE (GeditPreferencesDialog, gedit_preferences_dialog, GTK_TYPE_WINDOW) + + static void + gedit_preferences_dialog_dispose (GObject *object) + { + GeditPreferencesDialog *dlg = GEDIT_PREFERENCES_DIALOG (object); + + g_clear_object (&dlg->editor); + g_clear_object (&dlg->uisettings); + + G_OBJECT_CLASS (gedit_preferences_dialog_parent_class)->dispose (object); + } + + static void + gedit_preferences_dialog_close (GeditPreferencesDialog *dialog) +@@ -143,77 +154,79 @@ static void + gedit_preferences_dialog_class_init (GeditPreferencesDialogClass *klass) + { + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GtkBindingSet *binding_set; + + /* Otherwise libpeas-gtk might not be linked */ + g_type_ensure (PEAS_GTK_TYPE_PLUGIN_MANAGER); + + object_class->dispose = gedit_preferences_dialog_dispose; + + signals[CLOSE] = + g_signal_new_class_handler ("close", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_CALLBACK (gedit_preferences_dialog_close), + NULL, NULL, NULL, + G_TYPE_NONE, + 0); + + binding_set = gtk_binding_set_by_class (klass); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, "close", 0); + + /* Bind class to template */ + gtk_widget_class_set_template_from_resource (widget_class, + "/org/gnome/gedit/ui/gedit-preferences-dialog.ui"); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, notebook); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, display_line_numbers_checkbutton); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, display_statusbar_checkbutton); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, display_grid_checkbutton); ++ gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, display_overview_map_checkbutton); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, right_margin_checkbutton); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, right_margin_position_grid); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, right_margin_position_spinbutton); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, highlight_current_line_checkbutton); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, bracket_matching_checkbutton); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, wrap_text_checkbutton); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, split_checkbutton); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, tabs_width_spinbutton); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, insert_spaces_checkbutton); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, auto_indent_checkbutton); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, backup_copy_checkbutton); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, auto_save_checkbutton); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, auto_save_spinbutton); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, default_font_checkbutton); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, font_button); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, font_grid); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, schemes_list); ++ gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, schemes_scrolled_window); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, install_scheme_button); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, uninstall_scheme_button); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, schemes_toolbar); + gtk_widget_class_bind_template_child (widget_class, GeditPreferencesDialog, plugin_manager); + } + + static void + setup_editor_page (GeditPreferencesDialog *dlg) + { + gedit_debug (DEBUG_PREFS); + + /* Connect signal */ + g_settings_bind (dlg->editor, + GEDIT_SETTINGS_TABS_SIZE, + dlg->tabs_width_spinbutton, + "value", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + g_settings_bind (dlg->editor, + GEDIT_SETTINGS_INSERT_SPACES, + dlg->insert_spaces_checkbutton, + "active", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + g_settings_bind (dlg->editor, + GEDIT_SETTINGS_AUTO_INDENT, + dlg->auto_indent_checkbutton, + "active", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + g_settings_bind (dlg->editor, + GEDIT_SETTINGS_CREATE_BACKUP_COPY, + dlg->backup_copy_checkbutton, +@@ -359,488 +372,554 @@ setup_view_page (GeditPreferencesDialog *dlg) + gtk_toggle_button_set_inconsistent ( + GTK_TOGGLE_BUTTON (dlg->split_checkbutton), TRUE); + } + + gtk_toggle_button_set_active ( + GTK_TOGGLE_BUTTON (dlg->right_margin_checkbutton), + display_right_margin); + gtk_toggle_button_set_active ( + GTK_TOGGLE_BUTTON (dlg->display_grid_checkbutton), + background_pattern == GTK_SOURCE_BACKGROUND_PATTERN_TYPE_GRID); + + /* Set widgets sensitivity */ + gtk_widget_set_sensitive (dlg->split_checkbutton, + (wrap_mode != GTK_WRAP_NONE)); + + g_settings_bind (dlg->editor, + GEDIT_SETTINGS_DISPLAY_LINE_NUMBERS, + dlg->display_line_numbers_checkbutton, + "active", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + g_settings_bind (dlg->editor, + GEDIT_SETTINGS_HIGHLIGHT_CURRENT_LINE, + dlg->highlight_current_line_checkbutton, + "active", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + g_settings_bind (dlg->uisettings, + GEDIT_SETTINGS_STATUSBAR_VISIBLE, + dlg->display_statusbar_checkbutton, + "active", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); ++ g_settings_bind (dlg->editor, ++ GEDIT_SETTINGS_DISPLAY_OVERVIEW_MAP, ++ dlg->display_overview_map_checkbutton, ++ "active", ++ G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + g_settings_bind (dlg->editor, + GEDIT_SETTINGS_DISPLAY_RIGHT_MARGIN, + dlg->right_margin_checkbutton, + "active", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + g_settings_bind (dlg->editor, + GEDIT_SETTINGS_DISPLAY_RIGHT_MARGIN, + dlg->right_margin_position_grid, + "sensitive", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + g_settings_bind (dlg->editor, + GEDIT_SETTINGS_RIGHT_MARGIN_POSITION, + dlg->right_margin_position_spinbutton, + "value", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + g_settings_bind (dlg->editor, + GEDIT_SETTINGS_AUTO_SAVE_INTERVAL, + dlg->auto_save_spinbutton, + "value", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + g_signal_connect (dlg->wrap_text_checkbutton, + "toggled", + G_CALLBACK (wrap_mode_checkbutton_toggled), + dlg); + g_signal_connect (dlg->split_checkbutton, + "toggled", + G_CALLBACK (wrap_mode_checkbutton_toggled), + dlg); + g_signal_connect (dlg->display_grid_checkbutton, + "toggled", + G_CALLBACK (grid_checkbutton_toggled), + dlg); + } + + static void + setup_font_colors_page_font_section (GeditPreferencesDialog *dlg) + { + GeditSettings *settings; + gchar *system_font = NULL; + gchar *label; + + gedit_debug (DEBUG_PREFS); + + gtk_widget_set_tooltip_text (dlg->font_button, + _("Click on this button to select the font to be used by the editor")); + + /* Get values */ + settings = _gedit_settings_get_singleton (); +- system_font = _gedit_settings_get_system_font (settings); ++ system_font = gedit_settings_get_system_font (settings); + + label = g_strdup_printf(_("_Use the system fixed width font (%s)"), + system_font); + gtk_button_set_label (GTK_BUTTON (dlg->default_font_checkbutton), + label); + g_free (system_font); + g_free (label); + + /* Bind settings */ + g_settings_bind (dlg->editor, + GEDIT_SETTINGS_USE_DEFAULT_FONT, + dlg->default_font_checkbutton, + "active", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + g_settings_bind (dlg->editor, + GEDIT_SETTINGS_USE_DEFAULT_FONT, + dlg->font_grid, + "sensitive", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET | G_SETTINGS_BIND_INVERT_BOOLEAN); + g_settings_bind (dlg->editor, + GEDIT_SETTINGS_EDITOR_FONT, + dlg->font_button, + "font-name", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + } + + static void +-update_style_scheme_buttons_sensisitivity (GeditPreferencesDialog *dlg) ++set_buttons_sensisitivity_according_to_scheme (GeditPreferencesDialog *dlg, ++ GtkSourceStyleScheme *scheme) + { +- GtkSourceStyleScheme *selected_style_scheme; + gboolean editable = FALSE; + +- selected_style_scheme = gtk_source_style_scheme_chooser_get_style_scheme (GTK_SOURCE_STYLE_SCHEME_CHOOSER (dlg->schemes_list)); +- +- if (selected_style_scheme != NULL) ++ if (scheme != NULL) + { + const gchar *filename; + +- filename = gtk_source_style_scheme_get_filename (selected_style_scheme); ++ filename = gtk_source_style_scheme_get_filename (scheme); + if (filename != NULL) + { + editable = g_str_has_prefix (filename, gedit_dirs_get_user_styles_dir ()); + } + } + +- gtk_widget_set_sensitive (dlg->uninstall_scheme_button, editable); ++ gtk_widget_set_sensitive (dlg->uninstall_scheme_button, ++ editable); + } + + static void +-style_scheme_notify_cb (GtkSourceStyleSchemeChooser *chooser, +- GParamSpec *pspec, +- GeditPreferencesDialog *dlg) ++style_scheme_changed (GtkSourceStyleSchemeChooser *chooser, ++ GParamSpec *pspec, ++ GeditPreferencesDialog *dlg) + { +- update_style_scheme_buttons_sensisitivity (dlg); ++ GtkSourceStyleScheme *scheme; ++ const gchar *id; ++ ++ scheme = gtk_source_style_scheme_chooser_get_style_scheme (chooser); ++ id = gtk_source_style_scheme_get_id (scheme); ++ ++ g_settings_set_string (dlg->editor, GEDIT_SETTINGS_SCHEME, id); ++ set_buttons_sensisitivity_according_to_scheme (dlg, scheme); + } + +-static GFile * +-get_user_style_scheme_destination_file (GFile *src_file) ++static GtkSourceStyleScheme * ++get_default_color_scheme (GeditPreferencesDialog *dlg) + { +- gchar *basename; +- const gchar *styles_dir; +- GFile *dest_file; ++ GtkSourceStyleSchemeManager *manager; ++ GtkSourceStyleScheme *scheme = NULL; ++ gchar *pref_id; + +- basename = g_file_get_basename (src_file); +- g_return_val_if_fail (basename != NULL, NULL); ++ manager = gtk_source_style_scheme_manager_get_default (); + +- styles_dir = gedit_dirs_get_user_styles_dir (); +- dest_file = g_file_new_build_filename (styles_dir, basename, NULL); ++ pref_id = g_settings_get_string (dlg->editor, ++ GEDIT_SETTINGS_SCHEME); + +- g_free (basename); +- return dest_file; ++ scheme = gtk_source_style_scheme_manager_get_scheme (manager, ++ pref_id); ++ g_free (pref_id); ++ ++ if (scheme == NULL) ++ { ++ /* Fall-back to classic style scheme */ ++ scheme = gtk_source_style_scheme_manager_get_scheme (manager, ++ "classic"); ++ } ++ ++ return scheme; + } + +-/* Returns: whether @src_file has been correctly copied to @dest_file. */ ++/* ++ * file_copy: ++ * @name: a pointer to a %NULL-terminated string, that names ++ * the file to be copied, in the GLib file name encoding ++ * @dest_name: a pointer to a %NULL-terminated string, that is the ++ * name for the destination file, in the GLib file name encoding ++ * @error: return location for a #GError, or %NULL ++ * ++ * Copies file @name to @dest_name. ++ * ++ * If the call was successful, it returns %TRUE. If the call was not ++ * successful, it returns %FALSE and sets @error. The error domain ++ * is #G_FILE_ERROR. Possible error ++ * codes are those in the #GFileError enumeration. ++ * ++ * Return value: %TRUE on success, %FALSE otherwise. ++ */ + static gboolean +-copy_file (GFile *src_file, +- GFile *dest_file, +- GError **error) ++file_copy (const gchar *name, ++ const gchar *dest_name, ++ GError **error) + { +- if (g_file_equal (src_file, dest_file)) ++ gchar *contents; ++ gsize length; ++ gchar *dest_dir; ++ ++ /* FIXME - Paolo (Aug. 13, 2007): ++ * Since the style scheme files are relatively small, we can implement ++ * file copy getting all the content of the source file in a buffer and ++ * then write the content to the destination file. In this way we ++ * can use the g_file_get_contents and g_file_set_contents and avoid to ++ * write custom code to copy the file (with sane error management). ++ * If needed we can improve this code later. */ ++ ++ g_return_val_if_fail (name != NULL, FALSE); ++ g_return_val_if_fail (dest_name != NULL, FALSE); ++ g_return_val_if_fail (error == NULL || *error == NULL, FALSE); ++ ++ /* Note: we allow to copy a file to itself since this is not a problem ++ * in our use case */ ++ ++ /* Ensure the destination directory exists */ ++ dest_dir = g_path_get_dirname (dest_name); ++ ++ errno = 0; ++ if (g_mkdir_with_parents (dest_dir, 0755) != 0) + { ++ gint save_errno = errno; ++ gchar *display_filename = g_filename_display_name (dest_dir); ++ ++ g_set_error (error, ++ G_FILE_ERROR, ++ g_file_error_from_errno (save_errno), ++ _("Directory “%s” could not be created: g_mkdir_with_parents() failed: %s"), ++ display_filename, ++ g_strerror (save_errno)); ++ ++ g_free (dest_dir); ++ g_free (display_filename); ++ + return FALSE; + } + +- if (!tepl_utils_create_parent_directories (dest_file, NULL, error)) ++ g_free (dest_dir); ++ ++ if (!g_file_get_contents (name, &contents, &length, error)) ++ return FALSE; ++ ++ if (!g_file_set_contents (dest_name, contents, length, error)) + { ++ g_free (contents); + return FALSE; + } + +- return g_file_copy (src_file, +- dest_file, +- G_FILE_COPY_OVERWRITE | G_FILE_COPY_TARGET_DEFAULT_PERMS, +- NULL, /* cancellable */ +- NULL, NULL, /* progress callback */ +- error); ++ g_free (contents); ++ ++ return TRUE; + } + +-/* Get the style scheme ID of @user_style_scheme_file if it has been correctly +- * installed and @user_style_scheme_file is a valid style scheme file. ++/* ++ * install_style_scheme: ++ * @manager: a #GtkSourceStyleSchemeManager ++ * @fname: the file name of the style scheme to be installed ++ * ++ * Install a new user scheme. ++ * This function copies @fname in #GEDIT_STYLES_DIR and ask the style manager to ++ * recompute the list of available style schemes. It then checks if a style ++ * scheme with the right file name exists. ++ * ++ * If the call was succesful, it returns the id of the installed scheme ++ * otherwise %NULL. ++ * ++ * Return value: the id of the installed scheme, %NULL otherwise. + */ +-static const gchar * +-get_style_scheme_id_after_installing_user_style_scheme (GFile *user_style_scheme_file) ++static GtkSourceStyleScheme * ++install_style_scheme (const gchar *fname) + { + GtkSourceStyleSchemeManager *manager; +- const gchar * const *scheme_ids; +- gint i; ++ gchar *new_file_name = NULL; ++ gchar *dirname; ++ const gchar *styles_dir; ++ GError *error = NULL; ++ gboolean copied = FALSE; ++ const gchar * const *ids; ++ ++ g_return_val_if_fail (fname != NULL, NULL); + + manager = gtk_source_style_scheme_manager_get_default (); +- gtk_source_style_scheme_manager_force_rescan (manager); + +- scheme_ids = gtk_source_style_scheme_manager_get_scheme_ids (manager); ++ dirname = g_path_get_dirname (fname); ++ styles_dir = gedit_dirs_get_user_styles_dir (); + +- for (i = 0; scheme_ids != NULL && scheme_ids[i] != NULL; i++) ++ if (strcmp (dirname, styles_dir) != 0) + { +- const gchar *cur_scheme_id = scheme_ids[i]; +- GtkSourceStyleScheme *scheme; +- const gchar *filename; +- GFile *scheme_file; ++ gchar *basename; + +- scheme = gtk_source_style_scheme_manager_get_scheme (manager, cur_scheme_id); +- filename = gtk_source_style_scheme_get_filename (scheme); +- if (filename == NULL) +- { +- continue; +- } ++ basename = g_path_get_basename (fname); ++ new_file_name = g_build_filename (styles_dir, basename, NULL); ++ g_free (basename); + +- scheme_file = g_file_new_for_path (filename); +- if (g_file_equal (scheme_file, user_style_scheme_file)) ++ /* Copy the style scheme file into GEDIT_STYLES_DIR */ ++ if (!file_copy (fname, new_file_name, &error)) + { +- g_object_unref (scheme_file); +- return cur_scheme_id; ++ g_free (new_file_name); ++ g_free (dirname); ++ ++ g_message ("Cannot install style scheme:\n%s", ++ error->message); ++ ++ g_error_free (error); ++ ++ return NULL; + } + +- g_object_unref (scheme_file); ++ copied = TRUE; ++ } ++ else ++ { ++ new_file_name = g_strdup (fname); + } + +- return NULL; +-} +- +-/* Returns: (nullable): the installed style scheme ID, or %NULL on failure. */ +-static const gchar * +-install_style_scheme (GFile *src_file, +- GError **error) +-{ +- GFile *dest_file; +- gboolean copied; +- const gchar *installed_style_scheme_id = NULL; +- GError *my_error = NULL; ++ g_free (dirname); + +- g_return_val_if_fail (G_IS_FILE (src_file), NULL); +- g_return_val_if_fail (error == NULL || *error == NULL, NULL); ++ /* Reload the available style schemes */ ++ gtk_source_style_scheme_manager_force_rescan (manager); + +- dest_file = get_user_style_scheme_destination_file (src_file); +- g_return_val_if_fail (dest_file != NULL, NULL); ++ /* Check the new style scheme has been actually installed */ ++ ids = gtk_source_style_scheme_manager_get_scheme_ids (manager); + +- copied = copy_file (src_file, dest_file, &my_error); +- if (my_error != NULL) ++ while (*ids != NULL) + { +- g_propagate_error (error, my_error); +- g_object_unref (dest_file); +- return NULL; +- } ++ GtkSourceStyleScheme *scheme; ++ const gchar *filename; + +- installed_style_scheme_id = get_style_scheme_id_after_installing_user_style_scheme (dest_file); ++ scheme = gtk_source_style_scheme_manager_get_scheme (manager, *ids); + +- if (installed_style_scheme_id == NULL && copied) +- { +- /* The style scheme has not been correctly installed. */ +- g_file_delete (dest_file, NULL, &my_error); +- if (my_error != NULL) +- { +- gchar *dest_file_parse_name = g_file_get_parse_name (dest_file); ++ filename = gtk_source_style_scheme_get_filename (scheme); + +- g_warning ("Failed to delete the file “%s”: %s", +- dest_file_parse_name, +- my_error->message); ++ if (filename && (strcmp (filename, new_file_name) == 0)) ++ { ++ /* The style scheme has been correctly installed */ ++ g_free (new_file_name); + +- g_free (dest_file_parse_name); +- g_clear_error (&my_error); ++ return scheme; + } ++ ++ids; + } + +- g_object_unref (dest_file); +- return installed_style_scheme_id; ++ /* The style scheme has not been correctly installed */ ++ if (copied) ++ g_unlink (new_file_name); ++ ++ g_free (new_file_name); ++ ++ return NULL; + } + +-/* ++/** + * uninstall_style_scheme: ++ * @manager: a #GtkSourceStyleSchemeManager + * @scheme: a #GtkSourceStyleScheme + * + * Uninstall a user scheme. + * +- * Returns: %TRUE on success, %FALSE otherwise. ++ * If the call was succesful, it returns %TRUE ++ * otherwise %FALSE. ++ * ++ * Return value: %TRUE on success, %FALSE otherwise. + */ + static gboolean + uninstall_style_scheme (GtkSourceStyleScheme *scheme) + { + GtkSourceStyleSchemeManager *manager; + const gchar *filename; + + g_return_val_if_fail (GTK_SOURCE_IS_STYLE_SCHEME (scheme), FALSE); + + manager = gtk_source_style_scheme_manager_get_default (); + + filename = gtk_source_style_scheme_get_filename (scheme); + if (filename == NULL) + return FALSE; + + if (g_unlink (filename) == -1) + return FALSE; + + /* Reload the available style schemes */ + gtk_source_style_scheme_manager_force_rescan (manager); + + return TRUE; + } + + static void +-add_scheme_chooser_response_cb (GtkFileChooserNative *chooser, +- gint response_id, +- GeditPreferencesDialog *dialog) ++add_scheme_chooser_response_cb (GeditFileChooserDialog *chooser, ++ gint res_id, ++ GeditPreferencesDialog *dlg) + { + GFile *file; +- const gchar *scheme_id; +- GeditSettings *settings; +- GSettings *editor_settings; +- GError *error = NULL; ++ gchar *filename; ++ GtkSourceStyleScheme *scheme; + +- if (response_id != GTK_RESPONSE_ACCEPT) ++ if (res_id != GTK_RESPONSE_ACCEPT) + { ++ gedit_file_chooser_dialog_hide (chooser); + return; + } + +- file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (chooser)); ++ file = gedit_file_chooser_dialog_get_file (chooser); ++ + if (file == NULL) + { + return; + } + +- scheme_id = install_style_scheme (file, &error); ++ filename = g_file_get_path (file); + g_object_unref (file); + +- if (scheme_id == NULL) ++ if (filename == NULL) + { +- if (error != NULL) +- { +- tepl_utils_show_warning_dialog (GTK_WINDOW (dialog), +- _("The selected color scheme cannot be installed: %s"), +- error->message); +- } +- else +- { +- tepl_utils_show_warning_dialog (GTK_WINDOW (dialog), +- _("The selected color scheme cannot be installed.")); +- } ++ return; ++ } ++ ++ gedit_file_chooser_dialog_hide (chooser); ++ ++ scheme = install_style_scheme (filename); ++ g_free (filename); ++ ++ if (scheme == NULL) ++ { ++ gedit_warning (GTK_WINDOW (dlg), ++ _("The selected color scheme cannot be installed.")); + +- g_clear_error (&error); + return; + } + +- settings = _gedit_settings_get_singleton (); +- editor_settings = _gedit_settings_peek_editor_settings (settings); +- g_settings_set_string (editor_settings, GEDIT_SETTINGS_SCHEME, scheme_id); ++ g_settings_set_string (dlg->editor, GEDIT_SETTINGS_SCHEME, ++ gtk_source_style_scheme_get_id (scheme)); ++ ++ set_buttons_sensisitivity_according_to_scheme (dlg, scheme); + } + + static void + install_scheme_clicked (GtkButton *button, +- GeditPreferencesDialog *dialog) ++ GeditPreferencesDialog *dlg) + { +- GtkFileChooserNative *chooser; +- GtkFileFilter *scheme_filter; +- GtkFileFilter *all_filter; ++ GeditFileChooserDialog *chooser; + +- if (dialog->install_scheme_file_chooser != NULL) ++ if (dlg->install_scheme_file_schooser != NULL) + { +- gtk_native_dialog_show (GTK_NATIVE_DIALOG (dialog->install_scheme_file_chooser)); ++ gedit_file_chooser_dialog_show (dlg->install_scheme_file_schooser); + return; + } + +- chooser = gtk_file_chooser_native_new (_("Add Color Scheme"), +- GTK_WINDOW (dialog), +- GTK_FILE_CHOOSER_ACTION_OPEN, +- _("_Add Scheme"), +- _("_Cancel")); ++ chooser = gedit_file_chooser_dialog_create (_("Add Scheme"), ++ GTK_WINDOW (dlg), ++ GEDIT_FILE_CHOOSER_FLAG_OPEN, ++ _("_Cancel"), ++ _("A_dd Scheme")); + + /* Filters */ +- scheme_filter = gtk_file_filter_new (); +- gtk_file_filter_set_name (scheme_filter, _("Color Scheme Files")); +- gtk_file_filter_add_pattern (scheme_filter, "*.xml"); +- gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), scheme_filter); +- +- all_filter = gtk_file_filter_new (); +- gtk_file_filter_set_name (all_filter, _("All Files")); +- gtk_file_filter_add_pattern (all_filter, "*"); +- gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), all_filter); ++ gedit_file_chooser_dialog_add_pattern_filter (chooser, ++ _("Color Scheme Files"), ++ "*.xml"); + +- gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (chooser), scheme_filter); ++ gedit_file_chooser_dialog_add_pattern_filter (chooser, ++ _("All Files"), ++ "*"); + + g_signal_connect (chooser, + "response", + G_CALLBACK (add_scheme_chooser_response_cb), +- dialog); ++ dlg); + +- g_set_weak_pointer (&dialog->install_scheme_file_chooser, chooser); ++ dlg->install_scheme_file_schooser = chooser; + +- gtk_native_dialog_show (GTK_NATIVE_DIALOG (chooser)); ++ g_object_add_weak_pointer (G_OBJECT (chooser), ++ (gpointer) &dlg->install_scheme_file_schooser); ++ ++ gedit_file_chooser_dialog_show (chooser); + } + + static void + uninstall_scheme_clicked (GtkButton *button, + GeditPreferencesDialog *dlg) + { + GtkSourceStyleScheme *scheme; +- GtkSourceStyleScheme *new_selected_scheme; + + scheme = gtk_source_style_scheme_chooser_get_style_scheme (GTK_SOURCE_STYLE_SCHEME_CHOOSER (dlg->schemes_list)); + +- if (scheme == NULL) +- { +- return; +- } +- + if (!uninstall_style_scheme (scheme)) + { +- tepl_utils_show_warning_dialog (GTK_WINDOW (dlg), +- _("Could not remove color scheme “%s”."), +- gtk_source_style_scheme_get_name (scheme)); +- return; +- } +- +- new_selected_scheme = gtk_source_style_scheme_chooser_get_style_scheme (GTK_SOURCE_STYLE_SCHEME_CHOOSER (dlg->schemes_list)); +- if (new_selected_scheme == NULL) +- { +- GeditSettings *settings; +- GSettings *editor_settings; +- +- settings = _gedit_settings_get_singleton (); +- editor_settings = _gedit_settings_peek_editor_settings (settings); +- +- g_settings_reset (editor_settings, GEDIT_SETTINGS_SCHEME); ++ gedit_warning (GTK_WINDOW (dlg), ++ _("Could not remove color scheme “%s”."), ++ gtk_source_style_scheme_get_name (scheme)); + } + } + + static void + setup_font_colors_page_style_scheme_section (GeditPreferencesDialog *dlg) + { + GtkStyleContext *context; +- GeditSettings *settings; +- GSettings *editor_settings; ++ GtkSourceStyleScheme *scheme; + + gedit_debug (DEBUG_PREFS); + +- /* junction between the schemes list and the toolbar */ +- context = gtk_widget_get_style_context (dlg->schemes_list); ++ scheme = get_default_color_scheme (dlg); ++ ++ /* junction between the scrolled window and the toolbar */ ++ context = gtk_widget_get_style_context (dlg->schemes_scrolled_window); + gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM); + context = gtk_widget_get_style_context (dlg->schemes_toolbar); + gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP); + + /* Connect signals */ + g_signal_connect (dlg->schemes_list, + "notify::style-scheme", +- G_CALLBACK (style_scheme_notify_cb), ++ G_CALLBACK (style_scheme_changed), + dlg); + g_signal_connect (dlg->install_scheme_button, + "clicked", + G_CALLBACK (install_scheme_clicked), + dlg); + g_signal_connect (dlg->uninstall_scheme_button, + "clicked", + G_CALLBACK (uninstall_scheme_clicked), + dlg); + +- settings = _gedit_settings_get_singleton (); +- editor_settings = _gedit_settings_peek_editor_settings (settings); +- g_settings_bind (editor_settings, GEDIT_SETTINGS_SCHEME, +- dlg->schemes_list, "tepl-style-scheme-id", +- G_SETTINGS_BIND_DEFAULT); ++ gtk_source_style_scheme_chooser_set_style_scheme (GTK_SOURCE_STYLE_SCHEME_CHOOSER (dlg->schemes_list), ++ scheme); + +- update_style_scheme_buttons_sensisitivity (dlg); ++ /* Set initial widget sensitivity */ ++ set_buttons_sensisitivity_according_to_scheme (dlg, scheme); + } + + static void + setup_font_colors_page (GeditPreferencesDialog *dlg) + { + setup_font_colors_page_font_section (dlg); + setup_font_colors_page_style_scheme_section (dlg); + } + + static void + setup_plugins_page (GeditPreferencesDialog *dlg) + { + gtk_widget_show_all (dlg->plugin_manager); + } + + static void + gedit_preferences_dialog_init (GeditPreferencesDialog *dlg) + { + gedit_debug (DEBUG_PREFS); + + dlg->editor = g_settings_new ("org.gnome.gedit.preferences.editor"); + dlg->uisettings = g_settings_new ("org.gnome.gedit.preferences.ui"); + + gtk_widget_init_template (GTK_WIDGET (dlg)); + + setup_editor_page (dlg); + setup_view_page (dlg); + setup_font_colors_page (dlg); + setup_plugins_page (dlg); + } +diff --git a/gedit/gedit-print-job.c b/gedit/gedit-print-job.c +index 6083669f9..e6c1dcdcb 100644 +--- a/gedit/gedit-print-job.c ++++ b/gedit/gedit-print-job.c +@@ -1,56 +1,56 @@ + /* + * gedit-print-job.c + * This file is part of gedit + * + * Copyright (C) 2000-2001 Chema Celorio, Paolo Maggi + * Copyright (C) 2002-2008 Paolo Maggi + * Copyright (C) 2015 Sébastien Wilmet + * + * 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, see . + */ + + #include "gedit-print-job.h" + + #include +-#include ++#include + + #include "gedit-debug.h" + #include "gedit-document-private.h" + #include "gedit-print-preview.h" + #include "gedit-utils.h" + #include "gedit-dirs.h" + #include "gedit-settings.h" + + struct _GeditPrintJob + { + GObject parent_instance; + + GSettings *gsettings; + + GeditView *view; + + GtkPrintOperation *operation; + GtkSourcePrintCompositor *compositor; + + GtkWidget *preview; + + gchar *status_string; + gdouble progress; + + /* Widgets part of the custom print preferences widget. + * These pointers are valid just when the dialog is displayed. + */ + GtkToggleButton *syntax_checkbutton; + GtkToggleButton *page_header_checkbutton; + GtkToggleButton *line_numbers_checkbutton; +@@ -508,61 +508,61 @@ create_compositor (GeditPrintJob *job) + "tab-width", tab_width, + "highlight-syntax", syntax_hl, + "wrap-mode", wrap_mode, + "print-line-numbers", print_line_numbers, + "print-header", print_header, + "print-footer", FALSE, + "body-font-name", print_font_body, + "line-numbers-font-name", print_font_numbers, + "header-font-name", print_font_header, + NULL)); + + margin = g_settings_get_double (job->gsettings, GEDIT_SETTINGS_PRINT_MARGIN_LEFT); + gtk_source_print_compositor_set_left_margin (job->compositor, margin, GTK_UNIT_MM); + + margin = g_settings_get_double (job->gsettings, GEDIT_SETTINGS_PRINT_MARGIN_TOP); + gtk_source_print_compositor_set_top_margin (job->compositor, margin, GTK_UNIT_MM); + + margin = g_settings_get_double (job->gsettings, GEDIT_SETTINGS_PRINT_MARGIN_RIGHT); + gtk_source_print_compositor_set_right_margin (job->compositor, margin, GTK_UNIT_MM); + + margin = g_settings_get_double (job->gsettings, GEDIT_SETTINGS_PRINT_MARGIN_BOTTOM); + gtk_source_print_compositor_set_bottom_margin (job->compositor, margin, GTK_UNIT_MM); + + if (print_header) + { + gchar *doc_name; + gchar *name_to_display; + gchar *left; + + doc_name = _gedit_document_get_uri_for_display (GEDIT_DOCUMENT (buf)); +- name_to_display = tepl_utils_str_middle_truncate (doc_name, 60); ++ name_to_display = gedit_utils_str_middle_truncate (doc_name, 60); + + left = g_strdup_printf (_("File: %s"), name_to_display); + + gtk_source_print_compositor_set_header_format (job->compositor, + TRUE, + left, + NULL, + /* Translators: %N is the current page number, %Q is the total + * number of pages (ex. Page 2 of 10) + */ + _("Page %N of %Q")); + + g_free (doc_name); + g_free (name_to_display); + g_free (left); + } + + g_free (print_font_body); + g_free (print_font_header); + g_free (print_font_numbers); + } + + static void + begin_print_cb (GtkPrintOperation *operation, + GtkPrintContext *context, + GeditPrintJob *job) + { + create_compositor (job); + + job->progress = 0.0; +diff --git a/gedit/gedit-progress-info-bar.c b/gedit/gedit-progress-info-bar.c +new file mode 100644 +index 000000000..d547189fb +--- /dev/null ++++ b/gedit/gedit-progress-info-bar.c +@@ -0,0 +1,177 @@ ++/* ++ * gedit-progress-info-bar.c ++ * This file is part of gedit ++ * ++ * Copyright (C) 2005 - Paolo Maggi ++ * ++ * 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, see . ++ */ ++ ++#include "gedit-progress-info-bar.h" ++#include ++ ++enum { ++ PROP_0, ++ PROP_HAS_CANCEL_BUTTON, ++ LAST_PROP ++}; ++ ++static GParamSpec *properties[LAST_PROP]; ++ ++struct _GeditProgressInfoBar ++{ ++ GtkInfoBar parent_instance; ++ ++ GtkWidget *image; ++ GtkWidget *label; ++ GtkWidget *progress; ++}; ++ ++G_DEFINE_TYPE (GeditProgressInfoBar, gedit_progress_info_bar, GTK_TYPE_INFO_BAR) ++ ++static void ++gedit_progress_info_bar_set_has_cancel_button (GeditProgressInfoBar *bar, ++ gboolean has_button) ++{ ++ if (has_button) ++ { ++ gtk_info_bar_add_button (GTK_INFO_BAR (bar), _("_Cancel"), GTK_RESPONSE_CANCEL); ++ } ++} ++ ++static void ++gedit_progress_info_bar_set_property (GObject *object, ++ guint prop_id, ++ const GValue *value, ++ GParamSpec *pspec) ++{ ++ GeditProgressInfoBar *bar; ++ ++ bar = GEDIT_PROGRESS_INFO_BAR (object); ++ ++ switch (prop_id) ++ { ++ case PROP_HAS_CANCEL_BUTTON: ++ gedit_progress_info_bar_set_has_cancel_button (bar, ++ g_value_get_boolean (value)); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ ++static void ++gedit_progress_info_bar_class_init (GeditProgressInfoBarClass *klass) ++{ ++ GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ++ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); ++ ++ gobject_class->set_property = gedit_progress_info_bar_set_property; ++ ++ properties[PROP_HAS_CANCEL_BUTTON] = ++ g_param_spec_boolean ("has-cancel-button", ++ "Has Cancel Button", ++ "If the message bar has a cancel button", ++ TRUE, ++ G_PARAM_WRITABLE | ++ G_PARAM_CONSTRUCT_ONLY | ++ G_PARAM_STATIC_STRINGS); ++ ++ g_object_class_install_properties (gobject_class, LAST_PROP, properties); ++ ++ /* Bind class to template */ ++ gtk_widget_class_set_template_from_resource (widget_class, ++ "/org/gnome/gedit/ui/gedit-progress-info-bar.ui"); ++ gtk_widget_class_bind_template_child (widget_class, GeditProgressInfoBar, image); ++ gtk_widget_class_bind_template_child (widget_class, GeditProgressInfoBar, label); ++ gtk_widget_class_bind_template_child (widget_class, GeditProgressInfoBar, progress); ++} ++ ++static void ++gedit_progress_info_bar_init (GeditProgressInfoBar *bar) ++{ ++ gtk_widget_init_template (GTK_WIDGET (bar)); ++} ++ ++GtkWidget * ++gedit_progress_info_bar_new (const gchar *icon_name, ++ const gchar *markup, ++ gboolean has_cancel) ++{ ++ GeditProgressInfoBar *bar; ++ ++ g_return_val_if_fail (icon_name != NULL, NULL); ++ g_return_val_if_fail (markup != NULL, NULL); ++ ++ bar = GEDIT_PROGRESS_INFO_BAR (g_object_new (GEDIT_TYPE_PROGRESS_INFO_BAR, ++ "has-cancel-button", has_cancel, ++ NULL)); ++ ++ gedit_progress_info_bar_set_icon_name (bar, icon_name); ++ gedit_progress_info_bar_set_markup (bar, markup); ++ ++ return GTK_WIDGET (bar); ++} ++ ++void ++gedit_progress_info_bar_set_icon_name (GeditProgressInfoBar *bar, ++ const gchar *icon_name) ++{ ++ g_return_if_fail (GEDIT_IS_PROGRESS_INFO_BAR (bar)); ++ g_return_if_fail (icon_name != NULL); ++ ++ gtk_image_set_from_icon_name (GTK_IMAGE (bar->image), ++ icon_name, ++ GTK_ICON_SIZE_SMALL_TOOLBAR); ++} ++ ++void ++gedit_progress_info_bar_set_markup (GeditProgressInfoBar *bar, ++ const gchar *markup) ++{ ++ g_return_if_fail (GEDIT_IS_PROGRESS_INFO_BAR (bar)); ++ g_return_if_fail (markup != NULL); ++ ++ gtk_label_set_markup (GTK_LABEL (bar->label), markup); ++} ++ ++void ++gedit_progress_info_bar_set_text (GeditProgressInfoBar *bar, ++ const gchar *text) ++{ ++ g_return_if_fail (GEDIT_IS_PROGRESS_INFO_BAR (bar)); ++ g_return_if_fail (text != NULL); ++ ++ gtk_label_set_text (GTK_LABEL (bar->label), text); ++} ++ ++void ++gedit_progress_info_bar_set_fraction (GeditProgressInfoBar *bar, ++ gdouble fraction) ++{ ++ g_return_if_fail (GEDIT_IS_PROGRESS_INFO_BAR (bar)); ++ ++ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (bar->progress), fraction); ++} ++ ++void ++gedit_progress_info_bar_pulse (GeditProgressInfoBar *bar) ++{ ++ g_return_if_fail (GEDIT_IS_PROGRESS_INFO_BAR (bar)); ++ ++ gtk_progress_bar_pulse (GTK_PROGRESS_BAR (bar->progress)); ++} ++ ++/* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-progress-info-bar.h b/gedit/gedit-progress-info-bar.h +new file mode 100644 +index 000000000..0d820d8e4 +--- /dev/null ++++ b/gedit/gedit-progress-info-bar.h +@@ -0,0 +1,53 @@ ++/* ++ * gedit-progress-info-bar.h ++ * This file is part of gedit ++ * ++ * Copyright (C) 2005 - Paolo Maggi ++ * ++ * 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, see . ++ */ ++ ++#ifndef GEDIT_PROGRESS_INFO_BAR_H ++#define GEDIT_PROGRESS_INFO_BAR_H ++ ++#include ++ ++G_BEGIN_DECLS ++ ++#define GEDIT_TYPE_PROGRESS_INFO_BAR (gedit_progress_info_bar_get_type ()) ++G_DECLARE_FINAL_TYPE (GeditProgressInfoBar, gedit_progress_info_bar, GEDIT, PROGRESS_INFO_BAR, GtkInfoBar) ++ ++GtkWidget *gedit_progress_info_bar_new (const gchar *icon_name, ++ const gchar *markup, ++ gboolean has_cancel); ++ ++void gedit_progress_info_bar_set_icon_name (GeditProgressInfoBar *bar, ++ const gchar *icon_name); ++ ++void gedit_progress_info_bar_set_markup (GeditProgressInfoBar *bar, ++ const gchar *markup); ++ ++void gedit_progress_info_bar_set_text (GeditProgressInfoBar *bar, ++ const gchar *text); ++ ++void gedit_progress_info_bar_set_fraction (GeditProgressInfoBar *bar, ++ gdouble fraction); ++ ++void gedit_progress_info_bar_pulse (GeditProgressInfoBar *bar); ++ ++G_END_DECLS ++ ++#endif /* GEDIT_PROGRESS_INFO_BAR_H */ ++ ++/* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-recent-osx.c b/gedit/gedit-recent-osx.c +deleted file mode 100644 +index 6f8c8cd6e..000000000 +--- a/gedit/gedit-recent-osx.c ++++ /dev/null +@@ -1,249 +0,0 @@ +-/* +- * This file is part of gedit +- * +- * Copyright (C) 2005 - Paolo Maggi +- * Copyright (C) 2014 - Paolo Borelli +- * Copyright (C) 2014 - Jesse van den Kieboom +- * +- * 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, see . +- */ +- +-#include "gedit-recent-osx.h" +- +-static gint +-sort_recent_items_mru (GtkRecentInfo *a, +- GtkRecentInfo *b, +- gpointer unused) +-{ +- g_assert (a != NULL && b != NULL); +- return gtk_recent_info_get_modified (b) - gtk_recent_info_get_modified (a); +-} +- +-static void +-populate_filter_info (GtkRecentInfo *info, +- GtkRecentFilterInfo *filter_info, +- GtkRecentFilterFlags needed) +-{ +- filter_info->uri = gtk_recent_info_get_uri (info); +- filter_info->mime_type = gtk_recent_info_get_mime_type (info); +- +- filter_info->contains = GTK_RECENT_FILTER_URI | GTK_RECENT_FILTER_MIME_TYPE; +- +- if (needed & GTK_RECENT_FILTER_DISPLAY_NAME) +- { +- filter_info->display_name = gtk_recent_info_get_display_name (info); +- filter_info->contains |= GTK_RECENT_FILTER_DISPLAY_NAME; +- } +- else +- { +- filter_info->uri = NULL; +- } +- +- if (needed & GTK_RECENT_FILTER_APPLICATION) +- { +- filter_info->applications = (const gchar **) gtk_recent_info_get_applications (info, NULL); +- filter_info->contains |= GTK_RECENT_FILTER_APPLICATION; +- } +- else +- { +- filter_info->applications = NULL; +- } +- +- if (needed & GTK_RECENT_FILTER_GROUP) +- { +- filter_info->groups = (const gchar **) gtk_recent_info_get_groups (info, NULL); +- filter_info->contains |= GTK_RECENT_FILTER_GROUP; +- } +- else +- { +- filter_info->groups = NULL; +- } +- +- if (needed & GTK_RECENT_FILTER_AGE) +- { +- filter_info->age = gtk_recent_info_get_age (info); +- filter_info->contains |= GTK_RECENT_FILTER_AGE; +- } +- else +- { +- filter_info->age = -1; +- } +-} +- +-/* The GeditRecentConfiguration struct is allocated and owned by the caller */ +-void +-gedit_recent_configuration_init_default (GeditRecentConfiguration *config) +-{ +- config->manager = gtk_recent_manager_get_default (); +- +- if (config->filter != NULL) +- { +- g_object_unref (config->filter); +- } +- +- config->filter = gtk_recent_filter_new (); +- gtk_recent_filter_add_application (config->filter, g_get_application_name ()); +- gtk_recent_filter_add_mime_type (config->filter, "text/plain"); +- g_object_ref_sink (config->filter); +- +- config->limit = 5; +- config->show_not_found = TRUE; +- config->show_private = FALSE; +- config->local_only = FALSE; +- +- config->substring_filter = NULL; +-} +- +-/* The GeditRecentConfiguration struct is owned and destroyed by the caller */ +-void +-gedit_recent_configuration_destroy (GeditRecentConfiguration *config) +-{ +- g_clear_object (&config->filter); +- config->manager = NULL; +- +- g_clear_pointer (&config->substring_filter, (GDestroyNotify)g_free); +-} +- +-GList * +-gedit_recent_get_items (GeditRecentConfiguration *config) +-{ +- GtkRecentFilterFlags needed; +- GList *items; +- GList *retitems = NULL; +- gint length; +- char *substring_filter = NULL; +- +- if (config->limit == 0) +- { +- return NULL; +- } +- +- items = gtk_recent_manager_get_items (config->manager); +- +- if (!items) +- { +- return NULL; +- } +- +- needed = gtk_recent_filter_get_needed (config->filter); +- if (config->substring_filter && *config->substring_filter != '\0') +- { +- gchar *filter_normalized; +- +- filter_normalized = g_utf8_normalize (config->substring_filter, -1, G_NORMALIZE_ALL); +- substring_filter = g_utf8_casefold (filter_normalized, -1); +- g_free (filter_normalized); +- } +- +- while (items) +- { +- GtkRecentInfo *info; +- GtkRecentFilterInfo filter_info; +- gboolean is_filtered; +- +- info = items->data; +- is_filtered = FALSE; +- +- if (config->local_only && !gtk_recent_info_is_local (info)) +- { +- is_filtered = TRUE; +- } +- else if (!config->show_private && gtk_recent_info_get_private_hint (info)) +- { +- is_filtered = TRUE; +- } +- else if (!config->show_not_found && !gtk_recent_info_exists (info)) +- { +- is_filtered = TRUE; +- } +- else +- { +- if (substring_filter) +- { +- gchar *uri_normalized; +- gchar *uri_casefolded; +- +- uri_normalized = g_utf8_normalize (gtk_recent_info_get_uri_display (info), -1, G_NORMALIZE_ALL); +- uri_casefolded = g_utf8_casefold (uri_normalized, -1); +- g_free (uri_normalized); +- +- if (strstr (uri_casefolded, substring_filter) == NULL) +- { +- is_filtered = TRUE; +- } +- +- g_free (uri_casefolded); +- } +- +- if (!is_filtered) +- { +- populate_filter_info (info, &filter_info, needed); +- is_filtered = !gtk_recent_filter_filter (config->filter, &filter_info); +- +- /* these we own */ +- if (filter_info.applications) +- { +- g_strfreev ((gchar **) filter_info.applications); +- } +- +- if (filter_info.groups) +- { +- g_strfreev ((gchar **) filter_info.groups); +- } +- } +- } +- +- if (!is_filtered) +- { +- retitems = g_list_prepend (retitems, info); +- } +- else +- { +- gtk_recent_info_unref (info); +- } +- +- items = g_list_delete_link (items, items); +- } +- +- g_free (substring_filter); +- +- if (!retitems) +- { +- return NULL; +- } +- +- retitems = g_list_sort_with_data (retitems, (GCompareDataFunc) sort_recent_items_mru, NULL); +- length = g_list_length (retitems); +- +- if ((config->limit != -1) && (length > config->limit)) +- { +- GList *clamp, *l; +- +- clamp = g_list_nth (retitems, config->limit - 1); +- +- if (!clamp) +- { +- return retitems; +- } +- +- l = clamp->next; +- clamp->next = NULL; +- +- g_list_free_full (l, (GDestroyNotify) gtk_recent_info_unref); +- } +- +- return retitems; +-} +- +-/* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-recent-osx.h b/gedit/gedit-recent-osx.h +deleted file mode 100644 +index df77ca724..000000000 +--- a/gedit/gedit-recent-osx.h ++++ /dev/null +@@ -1,54 +0,0 @@ +-/* +- * This file is part of gedit +- * +- * Copyright (C) 2005 - Paolo Maggi +- * Copyright (C) 2014 - Paolo Borelli +- * Copyright (C) 2014 - Jesse van den Kieboom +- * +- * 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 +- * MERCHANWINDOWILITY 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, see . +- */ +- +-#ifndef GEDIT_RECENT_OSX_H +-#define GEDIT_RECENT_OSX_H +- +-#include +- +-G_BEGIN_DECLS +- +-/* TODO: this code can be simplified, the struct can be made private, the dead +- * code can be removed, etc. +- */ +- +-typedef struct +-{ +- GtkRecentManager *manager; +- GtkRecentFilter *filter; +- +- gint limit; +- gchar *substring_filter; +- +- guint show_private : 1; +- guint show_not_found : 1; +- guint local_only : 1; +-} GeditRecentConfiguration; +- +-void gedit_recent_configuration_init_default (GeditRecentConfiguration *config); +-void gedit_recent_configuration_destroy (GeditRecentConfiguration *config); +-GList *gedit_recent_get_items (GeditRecentConfiguration *config); +- +-G_END_DECLS +- +-#endif /* GEDIT_RECENT_OSX_H */ +- +-/* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-recent.c b/gedit/gedit-recent.c +index b1535d057..2a0063306 100644 +--- a/gedit/gedit-recent.c ++++ b/gedit/gedit-recent.c +@@ -1,92 +1,317 @@ + /* + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi + * Copyright (C) 2014 - Paolo Borelli + * Copyright (C) 2014 - Jesse van den Kieboom + * + * 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, see . + */ + + #include "gedit-recent.h" + + void + gedit_recent_add_document (GeditDocument *document) + { +- TeplFile *file; ++ GtkSourceFile *file; + GFile *location; + GtkRecentManager *recent_manager; + GtkRecentData recent_data; + gchar *uri; + static gchar *groups[2]; + + g_return_if_fail (GEDIT_IS_DOCUMENT (document)); + +- file = tepl_buffer_get_file (TEPL_BUFFER (document)); +- location = tepl_file_get_location (file); ++ file = gedit_document_get_file (document); ++ location = gtk_source_file_get_location (file); + + if (location == NULL) + { + return; + } + + recent_manager = gtk_recent_manager_get_default (); + + groups[0] = (gchar *) g_get_application_name (); + groups[1] = NULL; + + recent_data.display_name = NULL; + recent_data.description = NULL; + recent_data.mime_type = gedit_document_get_mime_type (document); + recent_data.app_name = (gchar *) g_get_application_name (); + recent_data.app_exec = g_strjoin (" ", g_get_prgname (), "%u", NULL); + recent_data.groups = groups; + recent_data.is_private = FALSE; + + uri = g_file_get_uri (location); + + if (!gtk_recent_manager_add_full (recent_manager, uri, &recent_data)) + { + g_warning ("Failed to add uri '%s' to the recent manager.", uri); + } + + g_free (uri); + g_free (recent_data.app_exec); + g_free (recent_data.mime_type); + } + + void + gedit_recent_remove_if_local (GFile *location) + { + g_return_if_fail (G_IS_FILE (location)); + + /* If a file is local chances are that if load/save fails the file has + * beed removed and the failure is permanent so we remove it from the + * list of recent files. For remote files the failure may be just + * transitory and we keep the file in the list. + */ + if (g_file_has_uri_scheme (location, "file")) + { + GtkRecentManager *recent_manager; + gchar *uri; + + recent_manager = gtk_recent_manager_get_default (); + + uri = g_file_get_uri (location); + gtk_recent_manager_remove_item (recent_manager, uri, NULL); + g_free (uri); + } + } + ++static gint ++sort_recent_items_mru (GtkRecentInfo *a, ++ GtkRecentInfo *b, ++ gpointer unused) ++{ ++ g_assert (a != NULL && b != NULL); ++ return gtk_recent_info_get_modified (b) - gtk_recent_info_get_modified (a); ++} ++ ++static void ++populate_filter_info (GtkRecentInfo *info, ++ GtkRecentFilterInfo *filter_info, ++ GtkRecentFilterFlags needed) ++{ ++ filter_info->uri = gtk_recent_info_get_uri (info); ++ filter_info->mime_type = gtk_recent_info_get_mime_type (info); ++ ++ filter_info->contains = GTK_RECENT_FILTER_URI | GTK_RECENT_FILTER_MIME_TYPE; ++ ++ if (needed & GTK_RECENT_FILTER_DISPLAY_NAME) ++ { ++ filter_info->display_name = gtk_recent_info_get_display_name (info); ++ filter_info->contains |= GTK_RECENT_FILTER_DISPLAY_NAME; ++ } ++ else ++ { ++ filter_info->uri = NULL; ++ } ++ ++ if (needed & GTK_RECENT_FILTER_APPLICATION) ++ { ++ filter_info->applications = (const gchar **) gtk_recent_info_get_applications (info, NULL); ++ filter_info->contains |= GTK_RECENT_FILTER_APPLICATION; ++ } ++ else ++ { ++ filter_info->applications = NULL; ++ } ++ ++ if (needed & GTK_RECENT_FILTER_GROUP) ++ { ++ filter_info->groups = (const gchar **) gtk_recent_info_get_groups (info, NULL); ++ filter_info->contains |= GTK_RECENT_FILTER_GROUP; ++ } ++ else ++ { ++ filter_info->groups = NULL; ++ } ++ ++ if (needed & GTK_RECENT_FILTER_AGE) ++ { ++ filter_info->age = gtk_recent_info_get_age (info); ++ filter_info->contains |= GTK_RECENT_FILTER_AGE; ++ } ++ else ++ { ++ filter_info->age = -1; ++ } ++} ++ ++/* The GeditRecentConfiguration struct is allocated and owned by the caller */ ++void ++gedit_recent_configuration_init_default (GeditRecentConfiguration *config) ++{ ++ config->manager = gtk_recent_manager_get_default (); ++ ++ if (config->filter != NULL) ++ { ++ g_object_unref (config->filter); ++ } ++ ++ config->filter = gtk_recent_filter_new (); ++ gtk_recent_filter_add_application (config->filter, g_get_application_name ()); ++ gtk_recent_filter_add_mime_type (config->filter, "text/plain"); ++ g_object_ref_sink (config->filter); ++ ++ config->limit = 5; ++ config->show_not_found = TRUE; ++ config->show_private = FALSE; ++ config->local_only = FALSE; ++ ++ config->substring_filter = NULL; ++} ++ ++/* The GeditRecentConfiguration struct is owned and destroyed by the caller */ ++void ++gedit_recent_configuration_destroy (GeditRecentConfiguration *config) ++{ ++ g_clear_object (&config->filter); ++ config->manager = NULL; ++ ++ g_clear_pointer (&config->substring_filter, (GDestroyNotify)g_free); ++} ++ ++GList * ++gedit_recent_get_items (GeditRecentConfiguration *config) ++{ ++ GtkRecentFilterFlags needed; ++ GList *items; ++ GList *retitems = NULL; ++ gint length; ++ char *substring_filter = NULL; ++ ++ if (config->limit == 0) ++ { ++ return NULL; ++ } ++ ++ items = gtk_recent_manager_get_items (config->manager); ++ ++ if (!items) ++ { ++ return NULL; ++ } ++ ++ needed = gtk_recent_filter_get_needed (config->filter); ++ if (config->substring_filter && *config->substring_filter != '\0') ++ { ++ gchar *filter_normalized; ++ ++ filter_normalized = g_utf8_normalize (config->substring_filter, -1, G_NORMALIZE_ALL); ++ substring_filter = g_utf8_casefold (filter_normalized, -1); ++ g_free (filter_normalized); ++ } ++ ++ while (items) ++ { ++ GtkRecentInfo *info; ++ GtkRecentFilterInfo filter_info; ++ gboolean is_filtered; ++ ++ info = items->data; ++ is_filtered = FALSE; ++ ++ if (config->local_only && !gtk_recent_info_is_local (info)) ++ { ++ is_filtered = TRUE; ++ } ++ else if (!config->show_private && gtk_recent_info_get_private_hint (info)) ++ { ++ is_filtered = TRUE; ++ } ++ else if (!config->show_not_found && !gtk_recent_info_exists (info)) ++ { ++ is_filtered = TRUE; ++ } ++ else ++ { ++ if (substring_filter) ++ { ++ gchar *uri_normalized; ++ gchar *uri_casefolded; ++ ++ uri_normalized = g_utf8_normalize (gtk_recent_info_get_uri_display (info), -1, G_NORMALIZE_ALL); ++ uri_casefolded = g_utf8_casefold (uri_normalized, -1); ++ g_free (uri_normalized); ++ ++ if (strstr (uri_casefolded, substring_filter) == NULL) ++ { ++ is_filtered = TRUE; ++ } ++ ++ g_free (uri_casefolded); ++ } ++ ++ if (!is_filtered) ++ { ++ populate_filter_info (info, &filter_info, needed); ++ is_filtered = !gtk_recent_filter_filter (config->filter, &filter_info); ++ ++ /* these we own */ ++ if (filter_info.applications) ++ { ++ g_strfreev ((gchar **) filter_info.applications); ++ } ++ ++ if (filter_info.groups) ++ { ++ g_strfreev ((gchar **) filter_info.groups); ++ } ++ } ++ } ++ ++ if (!is_filtered) ++ { ++ retitems = g_list_prepend (retitems, info); ++ } ++ else ++ { ++ gtk_recent_info_unref (info); ++ } ++ ++ items = g_list_delete_link (items, items); ++ } ++ ++ g_free (substring_filter); ++ ++ if (!retitems) ++ { ++ return NULL; ++ } ++ ++ retitems = g_list_sort_with_data (retitems, (GCompareDataFunc) sort_recent_items_mru, NULL); ++ length = g_list_length (retitems); ++ ++ if ((config->limit != -1) && (length > config->limit)) ++ { ++ GList *clamp, *l; ++ ++ clamp = g_list_nth (retitems, config->limit - 1); ++ ++ if (!clamp) ++ { ++ return retitems; ++ } ++ ++ l = clamp->next; ++ clamp->next = NULL; ++ ++ g_list_free_full (l, (GDestroyNotify) gtk_recent_info_unref); ++ } ++ ++ return retitems; ++} ++ + /* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-recent.h b/gedit/gedit-recent.h +index 068d89c72..5c72837ed 100644 +--- a/gedit/gedit-recent.h ++++ b/gedit/gedit-recent.h +@@ -1,38 +1,55 @@ + /* + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi + * Copyright (C) 2014 - Paolo Borelli + * Copyright (C) 2014 - Jesse van den Kieboom + * + * 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 + * MERCHANWINDOWILITY 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, see . + */ + + #ifndef GEDIT_RECENT_H + #define GEDIT_RECENT_H + + #include + #include + + G_BEGIN_DECLS + +-void gedit_recent_add_document (GeditDocument *document); ++typedef struct ++{ ++ GtkRecentManager *manager; ++ GtkRecentFilter *filter; + +-void gedit_recent_remove_if_local (GFile *location); ++ gint limit; ++ gchar *substring_filter; ++ ++ guint show_private : 1; ++ guint show_not_found : 1; ++ guint local_only : 1; ++} GeditRecentConfiguration; ++ ++void gedit_recent_add_document (GeditDocument *document); ++ ++void gedit_recent_remove_if_local (GFile *location); ++ ++void gedit_recent_configuration_init_default (GeditRecentConfiguration *config); ++void gedit_recent_configuration_destroy (GeditRecentConfiguration *config); ++GList *gedit_recent_get_items (GeditRecentConfiguration *config); + + G_END_DECLS + +-#endif /* GEDIT_RECENT_H */ ++#endif /* GEDIT_RECENT_H */ + + /* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-settings.c b/gedit/gedit-settings.c +index a762a1dc2..6fa300f29 100644 +--- a/gedit/gedit-settings.c ++++ b/gedit/gedit-settings.c +@@ -10,144 +10,184 @@ + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * gedit 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 gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + + #include "gedit-settings.h" + #include + #include "gedit-app.h" + + #define GEDIT_SETTINGS_SYSTEM_FONT "monospace-font-name" + + struct _GeditSettings + { + GObject parent_instance; + + GSettings *settings_interface; + GSettings *settings_editor; + GSettings *settings_ui; + GSettings *settings_file_chooser_state; + }; + +-enum +-{ +- SIGNAL_FONTS_CHANGED, +- N_SIGNALS +-}; +- +-static guint signals[N_SIGNALS]; ++/* GeditSettings is a singleton. */ + static GeditSettings *singleton = NULL; + + G_DEFINE_TYPE (GeditSettings, gedit_settings, G_TYPE_OBJECT) + + static void + gedit_settings_dispose (GObject *object) + { + GeditSettings *self = GEDIT_SETTINGS (object); + + g_clear_object (&self->settings_interface); + g_clear_object (&self->settings_editor); + g_clear_object (&self->settings_ui); + g_clear_object (&self->settings_file_chooser_state); + + G_OBJECT_CLASS (gedit_settings_parent_class)->dispose (object); + } + + static void + gedit_settings_finalize (GObject *object) + { + GeditSettings *self = GEDIT_SETTINGS (object); + + if (singleton == self) + { + singleton = NULL; + } + + G_OBJECT_CLASS (gedit_settings_parent_class)->finalize (object); + } + + static void + gedit_settings_class_init (GeditSettingsClass *klass) + { + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gedit_settings_dispose; + object_class->finalize = gedit_settings_finalize; ++} + +- /* This signal is emitted when the return value of +- * _gedit_settings_get_selected_font() has potentially changed. +- */ +- signals[SIGNAL_FONTS_CHANGED] = +- g_signal_new ("fonts-changed", +- G_TYPE_FROM_CLASS (klass), +- G_SIGNAL_RUN_FIRST, +- 0, +- NULL, NULL, NULL, +- G_TYPE_NONE, 0); ++static void ++set_font (GeditSettings *self, ++ const gchar *font) ++{ ++ guint tabs_size; ++ GList *views; ++ GList *l; ++ ++ tabs_size = g_settings_get_uint (self->settings_editor, GEDIT_SETTINGS_TABS_SIZE); ++ ++ views = gedit_app_get_views (GEDIT_APP (g_application_get_default ())); ++ ++ for (l = views; l != NULL; l = l->next) ++ { ++ /* Note: we use def=FALSE to avoid GeditView to query dconf. */ ++ gedit_view_set_font (GEDIT_VIEW (l->data), FALSE, font); ++ ++ /* FIXME: setting the tab width seems unrelated to set_font(). */ ++ gtk_source_view_set_tab_width (GTK_SOURCE_VIEW (l->data), tabs_size); ++ } ++ ++ g_list_free (views); + } + + static void +-system_font_changed_cb (GSettings *settings, ++on_system_font_changed (GSettings *settings, + const gchar *key, + GeditSettings *self) + { +- if (g_settings_get_boolean (self->settings_editor, GEDIT_SETTINGS_USE_DEFAULT_FONT)) ++ ++ gboolean use_default_font; ++ ++ use_default_font = g_settings_get_boolean (self->settings_editor, GEDIT_SETTINGS_USE_DEFAULT_FONT); ++ ++ if (use_default_font) + { +- g_signal_emit (self, signals[SIGNAL_FONTS_CHANGED], 0); ++ gchar *font; ++ ++ font = g_settings_get_string (settings, key); ++ set_font (self, font); ++ g_free (font); + } + } + + static void +-use_default_font_changed_cb (GSettings *settings, ++on_use_default_font_changed (GSettings *settings, + const gchar *key, + GeditSettings *self) + { +- g_signal_emit (self, signals[SIGNAL_FONTS_CHANGED], 0); ++ gboolean use_default_font; ++ gchar *font; ++ ++ use_default_font = g_settings_get_boolean (settings, key); ++ ++ if (use_default_font) ++ { ++ font = g_settings_get_string (self->settings_interface, GEDIT_SETTINGS_SYSTEM_FONT); ++ } ++ else ++ { ++ font = g_settings_get_string (self->settings_editor, GEDIT_SETTINGS_EDITOR_FONT); ++ } ++ ++ set_font (self, font); ++ ++ g_free (font); + } + + static void +-editor_font_changed_cb (GSettings *settings, ++on_editor_font_changed (GSettings *settings, + const gchar *key, + GeditSettings *self) + { +- if (!g_settings_get_boolean (self->settings_editor, GEDIT_SETTINGS_USE_DEFAULT_FONT)) ++ gboolean use_default_font; ++ ++ use_default_font = g_settings_get_boolean (self->settings_editor, GEDIT_SETTINGS_USE_DEFAULT_FONT); ++ ++ if (!use_default_font) + { +- g_signal_emit (self, signals[SIGNAL_FONTS_CHANGED], 0); ++ gchar *font; ++ ++ font = g_settings_get_string (settings, key); ++ set_font (self, font); ++ g_free (font); + } + } + + static void + on_auto_save_changed (GSettings *settings, + const gchar *key, + GeditSettings *self) + { + gboolean auto_save; + GList *docs; + GList *l; + + auto_save = g_settings_get_boolean (settings, key); + + docs = gedit_app_get_documents (GEDIT_APP (g_application_get_default ())); + + for (l = docs; l != NULL; l = l->next) + { + GeditTab *tab = gedit_tab_get_from_document (GEDIT_DOCUMENT (l->data)); + gedit_tab_set_auto_save_enabled (tab, auto_save); + } + + g_list_free (docs); + } + + static void + on_auto_save_interval_changed (GSettings *settings, + const gchar *key, + GeditSettings *self) + { +@@ -180,163 +220,146 @@ on_syntax_highlighting_changed (GSettings *settings, + + enable = g_settings_get_boolean (settings, key); + + docs = gedit_app_get_documents (GEDIT_APP (g_application_get_default ())); + + for (l = docs; l != NULL; l = l->next) + { + GtkSourceBuffer *buffer = GTK_SOURCE_BUFFER (l->data); + gtk_source_buffer_set_highlight_syntax (buffer, enable); + } + + g_list_free (docs); + + /* update the sensitivity of the Higlight Mode menu item */ + windows = gedit_app_get_main_windows (GEDIT_APP (g_application_get_default ())); + + for (l = windows; l != NULL; l = l->next) + { + GAction *action; + + action = g_action_map_lookup_action (G_ACTION_MAP (l->data), "highlight-mode"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), enable); + } + + g_list_free (windows); + } + + static void + gedit_settings_init (GeditSettings *self) + { +- self->settings_interface = g_settings_new ("org.gnome.desktop.interface"); +- + self->settings_editor = g_settings_new ("org.gnome.gedit.preferences.editor"); + self->settings_ui = g_settings_new ("org.gnome.gedit.preferences.ui"); + self->settings_file_chooser_state = g_settings_new ("org.gnome.gedit.state.file-chooser"); + +- g_signal_connect_object (self->settings_interface, +- "changed::" GEDIT_SETTINGS_SYSTEM_FONT, +- G_CALLBACK (system_font_changed_cb), +- self, +- 0); +- +- g_signal_connect_object (self->settings_editor, +- "changed::" GEDIT_SETTINGS_USE_DEFAULT_FONT, +- G_CALLBACK (use_default_font_changed_cb), +- self, +- 0); +- +- g_signal_connect_object (self->settings_editor, +- "changed::" GEDIT_SETTINGS_EDITOR_FONT, +- G_CALLBACK (editor_font_changed_cb), +- self, +- 0); +- +- g_signal_connect_object (self->settings_editor, +- "changed::auto-save", +- G_CALLBACK (on_auto_save_changed), +- self, +- 0); +- +- g_signal_connect_object (self->settings_editor, +- "changed::auto-save-interval", +- G_CALLBACK (on_auto_save_interval_changed), +- self, +- 0); +- +- g_signal_connect_object (self->settings_editor, +- "changed::syntax-highlighting", +- G_CALLBACK (on_syntax_highlighting_changed), +- self, +- 0); ++ self->settings_interface = g_settings_new ("org.gnome.desktop.interface"); ++ ++ g_signal_connect (self->settings_interface, ++ "changed::monospace-font-name", ++ G_CALLBACK (on_system_font_changed), ++ self); ++ ++ /* editor changes */ ++ ++ g_signal_connect (self->settings_editor, ++ "changed::use-default-font", ++ G_CALLBACK (on_use_default_font_changed), ++ self); ++ ++ g_signal_connect (self->settings_editor, ++ "changed::editor-font", ++ G_CALLBACK (on_editor_font_changed), ++ self); ++ ++ g_signal_connect (self->settings_editor, ++ "changed::auto-save", ++ G_CALLBACK (on_auto_save_changed), ++ self); ++ ++ g_signal_connect (self->settings_editor, ++ "changed::auto-save-interval", ++ G_CALLBACK (on_auto_save_interval_changed), ++ self); ++ ++ g_signal_connect (self->settings_editor, ++ "changed::syntax-highlighting", ++ G_CALLBACK (on_syntax_highlighting_changed), ++ self); + } + + GeditSettings * + _gedit_settings_get_singleton (void) + { + if (singleton == NULL) + { + singleton = g_object_new (GEDIT_TYPE_SETTINGS, NULL); + } + + return singleton; + } + + void + gedit_settings_unref_singleton (void) + { + if (singleton != NULL) + { + g_object_unref (singleton); + } + + /* singleton is not set to NULL here, it is set to NULL in + * gedit_settings_finalize() (i.e. when we are sure that the ref count + * reaches 0). + */ + } + + GSettings * + _gedit_settings_peek_editor_settings (GeditSettings *self) + { + g_return_val_if_fail (GEDIT_IS_SETTINGS (self), NULL); + + return self->settings_editor; + } + + GSettings * + _gedit_settings_peek_file_chooser_state_settings (GeditSettings *self) + { + g_return_val_if_fail (GEDIT_IS_SETTINGS (self), NULL); + + return self->settings_file_chooser_state; + } + + gchar * +-_gedit_settings_get_system_font (GeditSettings *self) ++gedit_settings_get_system_font (GeditSettings *self) + { + g_return_val_if_fail (GEDIT_IS_SETTINGS (self), NULL); + +- return g_settings_get_string (self->settings_interface, GEDIT_SETTINGS_SYSTEM_FONT); +-} +- +-gchar * +-_gedit_settings_get_selected_font (GeditSettings *self) +-{ +- g_return_val_if_fail (GEDIT_IS_SETTINGS (self), NULL); +- +- if (g_settings_get_boolean (self->settings_editor, GEDIT_SETTINGS_USE_DEFAULT_FONT)) +- { +- return _gedit_settings_get_system_font (self); +- } +- +- return g_settings_get_string (self->settings_editor, GEDIT_SETTINGS_EDITOR_FONT); ++ return g_settings_get_string (self->settings_interface, "monospace-font-name"); + } + + static gboolean + strv_is_empty (gchar **strv) + { + if (strv == NULL || strv[0] == NULL) + { + return TRUE; + } + + /* Contains one empty string. */ + if (strv[1] == NULL && strv[0][0] == '\0') + { + return TRUE; + } + + return FALSE; + } + + static GSList * + encoding_strv_to_list (const gchar * const *encoding_strv) + { + GSList *list = NULL; + gchar **p; + + for (p = (gchar **)encoding_strv; p != NULL && *p != NULL; p++) + { + const gchar *charset = *p; + const GtkSourceEncoding *encoding; + +diff --git a/gedit/gedit-settings.h b/gedit/gedit-settings.h +index a7993d5d1..a2fe9e47b 100644 +--- a/gedit/gedit-settings.h ++++ b/gedit/gedit-settings.h +@@ -16,90 +16,87 @@ + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + + #ifndef GEDIT_SETTINGS_H + #define GEDIT_SETTINGS_H + + #include + + G_BEGIN_DECLS + + #define GEDIT_TYPE_SETTINGS (gedit_settings_get_type ()) + + G_DECLARE_FINAL_TYPE (GeditSettings, gedit_settings, GEDIT, SETTINGS, GObject) + + G_GNUC_INTERNAL + GeditSettings * _gedit_settings_get_singleton (void); + + void gedit_settings_unref_singleton (void); + + G_GNUC_INTERNAL + GSettings * _gedit_settings_peek_editor_settings (GeditSettings *self); + + G_GNUC_INTERNAL + GSettings * _gedit_settings_peek_file_chooser_state_settings (GeditSettings *self); + +-G_GNUC_INTERNAL +-gchar * _gedit_settings_get_system_font (GeditSettings *self); +- +-G_GNUC_INTERNAL +-gchar * _gedit_settings_get_selected_font (GeditSettings *self); ++gchar * gedit_settings_get_system_font (GeditSettings *self); + + GSList * gedit_settings_get_candidate_encodings (gboolean *default_candidates); + + /* key constants */ + #define GEDIT_SETTINGS_USE_DEFAULT_FONT "use-default-font" + #define GEDIT_SETTINGS_EDITOR_FONT "editor-font" + #define GEDIT_SETTINGS_SCHEME "scheme" + #define GEDIT_SETTINGS_CREATE_BACKUP_COPY "create-backup-copy" + #define GEDIT_SETTINGS_AUTO_SAVE "auto-save" + #define GEDIT_SETTINGS_AUTO_SAVE_INTERVAL "auto-save-interval" + #define GEDIT_SETTINGS_MAX_UNDO_ACTIONS "max-undo-actions" + #define GEDIT_SETTINGS_WRAP_MODE "wrap-mode" + #define GEDIT_SETTINGS_WRAP_LAST_SPLIT_MODE "wrap-last-split-mode" + #define GEDIT_SETTINGS_TABS_SIZE "tabs-size" + #define GEDIT_SETTINGS_INSERT_SPACES "insert-spaces" + #define GEDIT_SETTINGS_AUTO_INDENT "auto-indent" + #define GEDIT_SETTINGS_DISPLAY_LINE_NUMBERS "display-line-numbers" + #define GEDIT_SETTINGS_HIGHLIGHT_CURRENT_LINE "highlight-current-line" + #define GEDIT_SETTINGS_BRACKET_MATCHING "bracket-matching" + #define GEDIT_SETTINGS_DISPLAY_RIGHT_MARGIN "display-right-margin" + #define GEDIT_SETTINGS_RIGHT_MARGIN_POSITION "right-margin-position" + #define GEDIT_SETTINGS_SMART_HOME_END "smart-home-end" + #define GEDIT_SETTINGS_RESTORE_CURSOR_POSITION "restore-cursor-position" + #define GEDIT_SETTINGS_SYNTAX_HIGHLIGHTING "syntax-highlighting" + #define GEDIT_SETTINGS_SEARCH_HIGHLIGHTING "search-highlighting" ++#define GEDIT_SETTINGS_DISPLAY_OVERVIEW_MAP "display-overview-map" + #define GEDIT_SETTINGS_BACKGROUND_PATTERN "background-pattern" + #define GEDIT_SETTINGS_STATUSBAR_VISIBLE "statusbar-visible" + #define GEDIT_SETTINGS_SIDE_PANEL_VISIBLE "side-panel-visible" + #define GEDIT_SETTINGS_BOTTOM_PANEL_VISIBLE "bottom-panel-visible" + #define GEDIT_SETTINGS_PRINT_SYNTAX_HIGHLIGHTING "print-syntax-highlighting" + #define GEDIT_SETTINGS_PRINT_HEADER "print-header" + #define GEDIT_SETTINGS_PRINT_WRAP_MODE "print-wrap-mode" + #define GEDIT_SETTINGS_PRINT_LINE_NUMBERS "print-line-numbers" + #define GEDIT_SETTINGS_PRINT_FONT_BODY_PANGO "print-font-body-pango" + #define GEDIT_SETTINGS_PRINT_FONT_HEADER_PANGO "print-font-header-pango" + #define GEDIT_SETTINGS_PRINT_FONT_NUMBERS_PANGO "print-font-numbers-pango" + #define GEDIT_SETTINGS_PRINT_MARGIN_LEFT "margin-left" + #define GEDIT_SETTINGS_PRINT_MARGIN_TOP "margin-top" + #define GEDIT_SETTINGS_PRINT_MARGIN_RIGHT "margin-right" + #define GEDIT_SETTINGS_PRINT_MARGIN_BOTTOM "margin-bottom" + #define GEDIT_SETTINGS_CANDIDATE_ENCODINGS "candidate-encodings" + #define GEDIT_SETTINGS_ACTIVE_PLUGINS "active-plugins" + #define GEDIT_SETTINGS_ENSURE_TRAILING_NEWLINE "ensure-trailing-newline" + + /* window state keys */ + #define GEDIT_SETTINGS_WINDOW_STATE "state" + #define GEDIT_SETTINGS_WINDOW_SIZE "size" + #define GEDIT_SETTINGS_SHOW_TABS_MODE "show-tabs-mode" + #define GEDIT_SETTINGS_SIDE_PANEL_SIZE "side-panel-size" + #define GEDIT_SETTINGS_SIDE_PANEL_ACTIVE_PAGE "side-panel-active-page" + #define GEDIT_SETTINGS_BOTTOM_PANEL_SIZE "bottom-panel-size" + #define GEDIT_SETTINGS_BOTTOM_PANEL_ACTIVE_PAGE "bottom-panel-active-page" + + /* file chooser state keys */ + #define GEDIT_SETTINGS_ACTIVE_FILE_FILTER "filter-id" +diff --git a/gedit/gedit-tab.c b/gedit/gedit-tab.c +index af72864e2..6bb38b160 100644 +--- a/gedit/gedit-tab.c ++++ b/gedit/gedit-tab.c +@@ -1,65 +1,65 @@ + /* + * gedit-tab.c + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi + * Copyright (C) 2014, 2015 - Sébastien Wilmet + * + * 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, see . + */ + + #include "gedit-tab.h" + #include "gedit-tab-private.h" + + #include + #include +-#include + + #include "gedit-app.h" + #include "gedit-app-private.h" + #include "gedit-recent.h" + #include "gedit-utils.h" + #include "gedit-io-error-info-bar.h" + #include "gedit-print-job.h" + #include "gedit-print-preview.h" ++#include "gedit-progress-info-bar.h" + #include "gedit-debug.h" + #include "gedit-document.h" + #include "gedit-document-private.h" + #include "gedit-enum-types.h" + #include "gedit-settings.h" + #include "gedit-view-frame.h" + + #define GEDIT_TAB_KEY "GEDIT_TAB_KEY" + + struct _GeditTab + { + GtkBox parent_instance; + + GeditTabState state; + + GSettings *editor_settings; + + GeditViewFrame *frame; + + GtkWidget *info_bar; + GtkWidget *info_bar_hidden; + + GeditPrintJob *print_job; + GtkWidget *print_preview; + + GtkSourceFileSaverFlags save_flags; + + guint idle_scroll; + + gint auto_save_interval; +@@ -547,63 +547,63 @@ gedit_tab_set_state (GeditTab *tab, + if (state == GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW) + { + gtk_widget_hide (GTK_WIDGET (tab->frame)); + } + else if (state != GEDIT_TAB_STATE_LOADING_ERROR) + { + gtk_widget_show (GTK_WIDGET (tab->frame)); + } + + set_cursor_according_to_state (GTK_TEXT_VIEW (gedit_tab_get_view (tab)), + state); + + update_auto_save_timeout (tab); + + g_object_notify_by_pspec (G_OBJECT (tab), properties[PROP_STATE]); + g_object_notify_by_pspec (G_OBJECT (tab), properties[PROP_CAN_CLOSE]); + } + + static void + document_location_notify_handler (GtkSourceFile *file, + GParamSpec *pspec, + GeditTab *tab) + { + gedit_debug (DEBUG_TAB); + + /* Notify the change in the location */ + g_object_notify_by_pspec (G_OBJECT (tab), properties[PROP_NAME]); + } + + static void +-document_shortname_notify_handler (TeplFile *file, +- GParamSpec *pspec, +- GeditTab *tab) ++document_shortname_notify_handler (GeditDocument *document, ++ GParamSpec *pspec, ++ GeditTab *tab) + { + gedit_debug (DEBUG_TAB); + + /* Notify the change in the shortname */ + g_object_notify_by_pspec (G_OBJECT (tab), properties[PROP_NAME]); + } + + static void + document_modified_changed (GtkTextBuffer *document, + GeditTab *tab) + { + g_object_notify_by_pspec (G_OBJECT (tab), properties[PROP_NAME]); + g_object_notify_by_pspec (G_OBJECT (tab), properties[PROP_CAN_CLOSE]); + } + + static void + set_info_bar (GeditTab *tab, + GtkWidget *info_bar, + GtkResponseType default_response) + { + gedit_debug (DEBUG_TAB); + + if (tab->info_bar == info_bar) + { + return; + } + + if (info_bar == NULL) + { + /* Don't destroy the old info_bar right away, +@@ -699,369 +699,369 @@ io_loading_error_info_bar_response (GtkWidget *info_bar, + g_task_return_boolean (loading_task, FALSE); + g_object_unref (loading_task); + break; + } + } + + static void + file_already_open_warning_info_bar_response (GtkWidget *info_bar, + gint response_id, + GeditTab *tab) + { + GeditView *view = gedit_tab_get_view (tab); + + if (response_id == GTK_RESPONSE_YES) + { + set_editable (tab, TRUE); + } + + set_info_bar (tab, NULL, GTK_RESPONSE_NONE); + + gtk_widget_grab_focus (GTK_WIDGET (view)); + } + + static void + load_cancelled (GtkWidget *bar, + gint response_id, + GTask *loading_task) + { + LoaderData *data = g_task_get_task_data (loading_task); + +- g_return_if_fail (TEPL_IS_PROGRESS_INFO_BAR (data->tab->info_bar)); ++ g_return_if_fail (GEDIT_IS_PROGRESS_INFO_BAR (data->tab->info_bar)); + + g_cancellable_cancel (g_task_get_cancellable (loading_task)); + remove_tab (data->tab); + } + + static void + unrecoverable_reverting_error_info_bar_response (GtkWidget *info_bar, + gint response_id, + GTask *loading_task) + { + LoaderData *data = g_task_get_task_data (loading_task); + GeditView *view; + + gedit_tab_set_state (data->tab, GEDIT_TAB_STATE_NORMAL); + + set_info_bar (data->tab, NULL, GTK_RESPONSE_NONE); + + view = gedit_tab_get_view (data->tab); + gtk_widget_grab_focus (GTK_WIDGET (view)); + + g_task_return_boolean (loading_task, FALSE); + g_object_unref (loading_task); + } + + #define MAX_MSG_LENGTH 100 + + static void + show_loading_info_bar (GTask *loading_task) + { + LoaderData *data = g_task_get_task_data (loading_task); +- TeplProgressInfoBar *bar; ++ GtkWidget *bar; + GeditDocument *doc; + gchar *name; + gchar *dirname = NULL; + gchar *msg = NULL; + gchar *name_markup; + gchar *dirname_markup; + gint len; + + if (data->tab->info_bar != NULL) + { + return; + } + + gedit_debug (DEBUG_TAB); + + doc = gedit_tab_get_document (data->tab); + + name = gedit_document_get_short_name_for_display (doc); + len = g_utf8_strlen (name, -1); + + /* if the name is awfully long, truncate it and be done with it, + * otherwise also show the directory (ellipsized if needed) + */ + if (len > MAX_MSG_LENGTH) + { + gchar *str; + +- str = tepl_utils_str_middle_truncate (name, MAX_MSG_LENGTH); ++ str = gedit_utils_str_middle_truncate (name, MAX_MSG_LENGTH); + g_free (name); + name = str; + } + else + { + GtkSourceFile *file = gedit_document_get_file (doc); + GFile *location = gtk_source_file_get_location (file); + + if (location != NULL) + { + gchar *str = gedit_utils_location_get_dirname_for_display (location); + + /* use the remaining space for the dir, but use a min of 20 chars + * so that we do not end up with a dirname like "(a...b)". + * This means that in the worst case when the filename is long 99 + * we have a title long 99 + 20, but I think it's a rare enough + * case to be acceptable. It's justa darn title afterall :) + */ +- dirname = tepl_utils_str_middle_truncate (str, +- MAX (20, MAX_MSG_LENGTH - len)); ++ dirname = gedit_utils_str_middle_truncate (str, ++ MAX (20, MAX_MSG_LENGTH - len)); + g_free (str); + } + } + + name_markup = g_markup_printf_escaped ("%s", name); + + if (data->tab->state == GEDIT_TAB_STATE_REVERTING) + { + if (dirname != NULL) + { + dirname_markup = g_markup_printf_escaped ("%s", dirname); + + /* Translators: the first %s is a file name (e.g. test.txt) the second one + is a directory (e.g. ssh://master.gnome.org/home/users/paolo) */ + msg = g_strdup_printf (_("Reverting %s from %s"), + name_markup, + dirname_markup); + g_free (dirname_markup); + } + else + { + msg = g_strdup_printf (_("Reverting %s"), name_markup); + } + +- bar = tepl_progress_info_bar_new ("document-revert", msg, TRUE); ++ bar = gedit_progress_info_bar_new ("document-revert", msg, TRUE); + } + else + { + if (dirname != NULL) + { + dirname_markup = g_markup_printf_escaped ("%s", dirname); + + /* Translators: the first %s is a file name (e.g. test.txt) the second one + is a directory (e.g. ssh://master.gnome.org/home/users/paolo) */ + msg = g_strdup_printf (_("Loading %s from %s"), + name_markup, + dirname_markup); + g_free (dirname_markup); + } + else + { + msg = g_strdup_printf (_("Loading %s"), name_markup); + } + +- bar = tepl_progress_info_bar_new ("document-open", msg, TRUE); ++ bar = gedit_progress_info_bar_new ("document-open", msg, TRUE); + } + + g_signal_connect_object (bar, + "response", + G_CALLBACK (load_cancelled), + loading_task, + 0); + +- set_info_bar (data->tab, GTK_WIDGET (bar), GTK_RESPONSE_NONE); ++ set_info_bar (data->tab, bar, GTK_RESPONSE_NONE); + + g_free (msg); + g_free (name); + g_free (name_markup); + g_free (dirname); + } + + static void + show_saving_info_bar (GTask *saving_task) + { + GeditTab *tab = g_task_get_source_object (saving_task); +- TeplProgressInfoBar *bar; ++ GtkWidget *bar; + GeditDocument *doc; + gchar *short_name; + gchar *from; + gchar *to = NULL; + gchar *from_markup; + gchar *to_markup; + gchar *msg = NULL; + gint len; + + if (tab->info_bar != NULL) + { + return; + } + + gedit_debug (DEBUG_TAB); + + doc = gedit_tab_get_document (tab); + + short_name = gedit_document_get_short_name_for_display (doc); + + len = g_utf8_strlen (short_name, -1); + + /* if the name is awfully long, truncate it and be done with it, + * otherwise also show the directory (ellipsized if needed) + */ + if (len > MAX_MSG_LENGTH) + { +- from = tepl_utils_str_middle_truncate (short_name, MAX_MSG_LENGTH); ++ from = gedit_utils_str_middle_truncate (short_name, MAX_MSG_LENGTH); + g_free (short_name); + } + else + { + gchar *str; + SaverData *data; + GFile *location; + + data = g_task_get_task_data (saving_task); + location = gtk_source_file_saver_get_location (data->saver); + + from = short_name; + to = g_file_get_parse_name (location); +- str = tepl_utils_str_middle_truncate (to, MAX (20, MAX_MSG_LENGTH - len)); ++ str = gedit_utils_str_middle_truncate (to, MAX (20, MAX_MSG_LENGTH - len)); + g_free (to); + + to = str; + } + + from_markup = g_markup_printf_escaped ("%s", from); + + if (to != NULL) + { + to_markup = g_markup_printf_escaped ("%s", to); + + /* Translators: the first %s is a file name (e.g. test.txt) the second one + is a directory (e.g. ssh://master.gnome.org/home/users/paolo) */ + msg = g_strdup_printf (_("Saving %s to %s"), from_markup, to_markup); + g_free (to_markup); + } + else + { + msg = g_strdup_printf (_("Saving %s"), from_markup); + } + +- bar = tepl_progress_info_bar_new ("document-save", msg, FALSE); ++ bar = gedit_progress_info_bar_new ("document-save", msg, FALSE); + +- set_info_bar (tab, GTK_WIDGET (bar), GTK_RESPONSE_NONE); ++ set_info_bar (tab, bar, GTK_RESPONSE_NONE); + + g_free (msg); + g_free (to); + g_free (from); + g_free (from_markup); + } + + static void + info_bar_set_progress (GeditTab *tab, + goffset size, + goffset total_size) + { +- TeplProgressInfoBar *progress_info_bar; ++ GeditProgressInfoBar *progress_info_bar; + + if (tab->info_bar == NULL) + { + return; + } + + gedit_debug_message (DEBUG_TAB, "%" G_GOFFSET_FORMAT "/%" G_GOFFSET_FORMAT, size, total_size); + +- g_return_if_fail (TEPL_IS_PROGRESS_INFO_BAR (tab->info_bar)); ++ g_return_if_fail (GEDIT_IS_PROGRESS_INFO_BAR (tab->info_bar)); + +- progress_info_bar = TEPL_PROGRESS_INFO_BAR (tab->info_bar); ++ progress_info_bar = GEDIT_PROGRESS_INFO_BAR (tab->info_bar); + + if (total_size != 0) + { + gdouble frac = (gdouble)size / (gdouble)total_size; + +- tepl_progress_info_bar_set_fraction (progress_info_bar, frac); ++ gedit_progress_info_bar_set_fraction (progress_info_bar, frac); + } + else if (size != 0) + { +- tepl_progress_info_bar_pulse (progress_info_bar); ++ gedit_progress_info_bar_pulse (progress_info_bar); + } + else + { +- tepl_progress_info_bar_set_fraction (progress_info_bar, 0); ++ gedit_progress_info_bar_set_fraction (progress_info_bar, 0); + } + } + + /* Returns whether progress info should be shown. */ + static gboolean + should_show_progress_info (GTimer **timer, + goffset size, + goffset total_size) + { + gdouble elapsed_time; + gdouble total_time; + gdouble remaining_time; + + g_assert (timer != NULL); + + if (*timer == NULL) + { + return TRUE; + } + + elapsed_time = g_timer_elapsed (*timer, NULL); + + /* Wait a little, because at the very beginning it's maybe not very + * accurate (it takes initially more time for the first bytes, the + * following chunks should arrive more quickly, as a rough guess). + */ + if (elapsed_time < 0.5) + { + return FALSE; + } + + /* elapsed_time / total_time = size / total_size */ + total_time = (elapsed_time * total_size) / size; + + remaining_time = total_time - elapsed_time; + + /* Approximately more than 3 seconds remaining. */ + if (remaining_time > 3.0) + { + /* Once the progress info bar is shown, it must remain + * shown until the end, so we don't need the timer + * anymore. + */ + g_timer_destroy (*timer); + *timer = NULL; + + return TRUE; + } + + return FALSE; + } + + static gboolean + scroll_to_cursor (GeditTab *tab) + { + GeditView *view; + + view = gedit_tab_get_view (tab); +- tepl_view_scroll_to_cursor (TEPL_VIEW (view)); ++ gedit_view_scroll_to_cursor (view); + + tab->idle_scroll = 0; + return G_SOURCE_REMOVE; + } + + static void + unrecoverable_saving_error_info_bar_response (GtkWidget *info_bar, + gint response_id, + GTask *saving_task) + { + GeditTab *tab = g_task_get_source_object (saving_task); + GeditView *view; + + gedit_tab_set_state (tab, GEDIT_TAB_STATE_NORMAL); + + set_info_bar (tab, NULL, GTK_RESPONSE_NONE); + + view = gedit_tab_get_view (tab); + gtk_widget_grab_focus (GTK_WIDGET (view)); + + g_task_return_boolean (saving_task, FALSE); + g_object_unref (saving_task); + } + + /* Sets the save flags after an info bar response. */ + static void + response_set_save_flags (GTask *saving_task, + GtkSourceFileSaverFlags save_flags) + { + GeditTab *tab = g_task_get_source_object (saving_task); +@@ -1091,63 +1091,63 @@ response_set_save_flags (GTask *saving_task, + static void + invalid_character_info_bar_response (GtkWidget *info_bar, + gint response_id, + GTask *saving_task) + { + if (response_id == GTK_RESPONSE_YES) + { + GeditTab *tab = g_task_get_source_object (saving_task); + SaverData *data = g_task_get_task_data (saving_task); + GtkSourceFileSaverFlags save_flags; + + set_info_bar (tab, NULL, GTK_RESPONSE_NONE); + + /* Don't bug the user again with this... */ + tab->save_flags |= GTK_SOURCE_FILE_SAVER_FLAGS_IGNORE_INVALID_CHARS; + + save_flags = gtk_source_file_saver_get_flags (data->saver); + save_flags |= GTK_SOURCE_FILE_SAVER_FLAGS_IGNORE_INVALID_CHARS; + response_set_save_flags (saving_task, save_flags); + + /* Force saving */ + launch_saver (saving_task); + } + else + { + unrecoverable_saving_error_info_bar_response (info_bar, response_id, saving_task); + } + } + + static void +-cant_create_backup_error_info_bar_response (GtkWidget *info_bar, +- gint response_id, +- GTask *saving_task) ++no_backup_error_info_bar_response (GtkWidget *info_bar, ++ gint response_id, ++ GTask *saving_task) + { + if (response_id == GTK_RESPONSE_YES) + { + GeditTab *tab = g_task_get_source_object (saving_task); + SaverData *data = g_task_get_task_data (saving_task); + GtkSourceFileSaverFlags save_flags; + + set_info_bar (tab, NULL, GTK_RESPONSE_NONE); + + data->force_no_backup = TRUE; + save_flags = gtk_source_file_saver_get_flags (data->saver); + response_set_save_flags (saving_task, save_flags); + + /* Force saving */ + launch_saver (saving_task); + } + else + { + unrecoverable_saving_error_info_bar_response (info_bar, response_id, saving_task); + } + } + + static void + externally_modified_error_info_bar_response (GtkWidget *info_bar, + gint response_id, + GTask *saving_task) + { + if (response_id == GTK_RESPONSE_YES) + { + GeditTab *tab = g_task_get_source_object (saving_task); +@@ -1199,77 +1199,77 @@ recoverable_saving_error_info_bar_response (GtkWidget *info_bar, + + static void + externally_modified_notification_info_bar_response (GtkWidget *info_bar, + gint response_id, + GeditTab *tab) + { + GeditView *view; + + set_info_bar (tab, NULL, GTK_RESPONSE_NONE); + + view = gedit_tab_get_view (tab); + + if (response_id == GTK_RESPONSE_OK) + { + _gedit_tab_revert (tab); + } + else + { + tab->ask_if_externally_modified = FALSE; + + /* go back to normal state */ + gedit_tab_set_state (tab, GEDIT_TAB_STATE_NORMAL); + } + + gtk_widget_grab_focus (GTK_WIDGET (view)); + } + + static void + display_externally_modified_notification (GeditTab *tab) + { +- TeplInfoBar *info_bar; ++ GtkWidget *info_bar; + GeditDocument *doc; + GtkSourceFile *file; + GFile *location; + gboolean document_modified; + + doc = gedit_tab_get_document (tab); + file = gedit_document_get_file (doc); + + /* we're here because the file we're editing changed on disk */ + location = gtk_source_file_get_location (file); + g_return_if_fail (location != NULL); + + document_modified = gtk_text_buffer_get_modified (GTK_TEXT_BUFFER (doc)); +- info_bar = tepl_io_error_info_bar_externally_modified (location, document_modified); ++ info_bar = gedit_externally_modified_info_bar_new (location, document_modified); + +- set_info_bar (tab, GTK_WIDGET (info_bar), GTK_RESPONSE_OK); ++ set_info_bar (tab, info_bar, GTK_RESPONSE_OK); + + g_signal_connect (info_bar, + "response", + G_CALLBACK (externally_modified_notification_info_bar_response), + tab); + } + + static gboolean + view_focused_in (GtkWidget *widget, + GdkEventFocus *event, + GeditTab *tab) + { + GeditDocument *doc; + GtkSourceFile *file; + + g_return_val_if_fail (GEDIT_IS_TAB (tab), GDK_EVENT_PROPAGATE); + + /* we try to detect file changes only in the normal state */ + if (tab->state != GEDIT_TAB_STATE_NORMAL) + { + return GDK_EVENT_PROPAGATE; + } + + /* we already asked, don't bug the user again */ + if (!tab->ask_if_externally_modified) + { + return GDK_EVENT_PROPAGATE; + } + + doc = gedit_tab_get_document (tab); +@@ -1280,104 +1280,101 @@ view_focused_in (GtkWidget *widget, + { + gtk_source_file_check_file_on_disk (file); + + if (gtk_source_file_is_externally_modified (file)) + { + gedit_tab_set_state (tab, GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION); + + display_externally_modified_notification (tab); + } + } + + return GDK_EVENT_PROPAGATE; + } + + static void + on_drop_uris (GeditView *view, + gchar **uri_list, + GeditTab *tab) + { + g_signal_emit (G_OBJECT (tab), signals[DROP_URIS], 0, uri_list); + } + + static void + gedit_tab_init (GeditTab *tab) + { + gboolean auto_save; + gint auto_save_interval; + GeditDocument *doc; + GeditView *view; + GtkSourceFile *file; +- TeplFile *tepl_file; + + tab->state = GEDIT_TAB_STATE_NORMAL; + + tab->editor_settings = g_settings_new ("org.gnome.gedit.preferences.editor"); + + tab->editable = TRUE; + + tab->ask_if_externally_modified = TRUE; + + gtk_orientable_set_orientation (GTK_ORIENTABLE (tab), + GTK_ORIENTATION_VERTICAL); + + /* Manage auto save data */ + auto_save = g_settings_get_boolean (tab->editor_settings, + GEDIT_SETTINGS_AUTO_SAVE); + g_settings_get (tab->editor_settings, GEDIT_SETTINGS_AUTO_SAVE_INTERVAL, + "u", &auto_save_interval); + tab->auto_save = auto_save != FALSE; + tab->auto_save_interval = auto_save_interval; + + /* Create the frame */ + tab->frame = gedit_view_frame_new (); + gtk_widget_show (GTK_WIDGET (tab->frame)); + + gtk_box_pack_end (GTK_BOX (tab), GTK_WIDGET (tab->frame), TRUE, TRUE, 0); + + doc = gedit_tab_get_document (tab); + g_object_set_data (G_OBJECT (doc), GEDIT_TAB_KEY, tab); + + file = gedit_document_get_file (doc); +- tepl_file = tepl_buffer_get_file (TEPL_BUFFER (doc)); + + g_signal_connect_object (file, + "notify::location", + G_CALLBACK (document_location_notify_handler), + tab, + 0); + +- g_signal_connect_object (tepl_file, +- "notify::short-name", +- G_CALLBACK (document_shortname_notify_handler), +- tab, +- 0); ++ g_signal_connect (doc, ++ "notify::shortname", ++ G_CALLBACK (document_shortname_notify_handler), ++ tab); + + g_signal_connect (doc, + "modified_changed", + G_CALLBACK (document_modified_changed), + tab); + + view = gedit_tab_get_view (tab); + + g_signal_connect_after (view, + "focus-in-event", + G_CALLBACK (view_focused_in), + tab); + + g_signal_connect_after (view, + "realize", + G_CALLBACK (view_realized), + tab); + + g_signal_connect (view, + "drop-uris", + G_CALLBACK (on_drop_uris), + tab); + } + + GeditTab * + _gedit_tab_new (void) + { + return g_object_new (GEDIT_TYPE_TAB, NULL); + } + +@@ -1407,94 +1404,94 @@ gedit_tab_get_view (GeditTab *tab) + */ + GeditDocument * + gedit_tab_get_document (GeditTab *tab) + { + GeditView *view; + + g_return_val_if_fail (GEDIT_IS_TAB (tab), NULL); + + view = gedit_view_frame_get_view (tab->frame); + + return GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))); + } + + #define MAX_DOC_NAME_LENGTH 40 + + gchar * + _gedit_tab_get_name (GeditTab *tab) + { + GeditDocument *doc; + gchar *name; + gchar *docname; + gchar *tab_name; + + g_return_val_if_fail (GEDIT_IS_TAB (tab), NULL); + + doc = gedit_tab_get_document (tab); + + name = gedit_document_get_short_name_for_display (doc); + + /* Truncate the name so it doesn't get insanely wide. */ +- docname = tepl_utils_str_middle_truncate (name, MAX_DOC_NAME_LENGTH); ++ docname = gedit_utils_str_middle_truncate (name, MAX_DOC_NAME_LENGTH); + + if (gtk_text_buffer_get_modified (GTK_TEXT_BUFFER (doc))) + { + tab_name = g_strdup_printf ("*%s", docname); + } + else + { + tab_name = g_strdup (docname); + } + + g_free (docname); + g_free (name); + + return tab_name; + } + + gchar * + _gedit_tab_get_tooltip (GeditTab *tab) + { + GeditDocument *doc; + gchar *tip; + gchar *uri; + gchar *ruri; + gchar *ruri_markup; + + g_return_val_if_fail (GEDIT_IS_TAB (tab), NULL); + + doc = gedit_tab_get_document (tab); + + uri = _gedit_document_get_uri_for_display (doc); + g_return_val_if_fail (uri != NULL, NULL); + +- ruri = tepl_utils_replace_home_dir_with_tilde (uri); ++ ruri = gedit_utils_replace_home_dir_with_tilde (uri); + g_free (uri); + + ruri_markup = g_markup_printf_escaped ("%s", ruri); + + switch (tab->state) + { + gchar *content_type; + gchar *mime_type; + gchar *content_description; + gchar *content_full_description; + gchar *encoding; + GtkSourceFile *file; + const GtkSourceEncoding *enc; + + case GEDIT_TAB_STATE_LOADING_ERROR: + tip = g_strdup_printf (_("Error opening file %s"), + ruri_markup); + break; + + case GEDIT_TAB_STATE_REVERTING_ERROR: + tip = g_strdup_printf (_("Error reverting file %s"), + ruri_markup); + break; + + case GEDIT_TAB_STATE_SAVING_ERROR: + tip = g_strdup_printf (_("Error saving file %s"), + ruri_markup); + break; + default: + content_type = gedit_document_get_content_type (doc); +@@ -1606,65 +1603,63 @@ gedit_tab_get_from_document (GeditDocument *doc) + return g_object_get_data (G_OBJECT (doc), GEDIT_TAB_KEY); + } + + static void + loader_progress_cb (goffset size, + goffset total_size, + GTask *loading_task) + { + LoaderData *data = g_task_get_task_data (loading_task); + + g_return_if_fail (data->tab->state == GEDIT_TAB_STATE_LOADING || + data->tab->state == GEDIT_TAB_STATE_REVERTING); + + if (should_show_progress_info (&data->timer, size, total_size)) + { + show_loading_info_bar (loading_task); + info_bar_set_progress (data->tab, size, total_size); + } + } + + static void + goto_line (GTask *loading_task) + { + LoaderData *data = g_task_get_task_data (loading_task); + GeditDocument *doc = gedit_tab_get_document (data->tab); + GtkTextIter iter; + + /* Move the cursor at the requested line if any. */ + if (data->line_pos > 0) + { +- TeplView *view = TEPL_VIEW (gedit_tab_get_view (data->tab)); +- +- tepl_view_goto_line_offset (view, +- data->line_pos - 1, +- MAX (0, data->column_pos - 1)); ++ gedit_document_goto_line_offset (doc, ++ data->line_pos - 1, ++ MAX (0, data->column_pos - 1)); + return; + } + + /* If enabled, move to the position stored in the metadata. */ + if (g_settings_get_boolean (data->tab->editor_settings, GEDIT_SETTINGS_RESTORE_CURSOR_POSITION)) + { + gchar *pos; + gint offset; + + pos = gedit_document_get_metadata (doc, GEDIT_METADATA_ATTRIBUTE_POSITION); + + offset = pos != NULL ? atoi (pos) : 0; + g_free (pos); + + gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (doc), + &iter, + MAX (0, offset)); + + /* make sure it's a valid position, if the file + * changed we may have ended up in the middle of + * a utf8 character cluster */ + if (!gtk_text_iter_is_cursor_position (&iter)) + { + gtk_text_iter_set_line_offset (&iter, 0); + } + } + + /* Otherwise to the top. */ + else + { +@@ -1726,72 +1721,72 @@ successful_load (GTask *loading_task) + + if (data->user_requested_encoding) + { + const GtkSourceEncoding *encoding = gtk_source_file_loader_get_encoding (data->loader); + const gchar *charset = gtk_source_encoding_get_charset (encoding); + + gedit_document_set_metadata (doc, + GEDIT_METADATA_ATTRIBUTE_ENCODING, charset, + NULL); + } + + goto_line (loading_task); + + /* Scroll to the cursor when the document is loaded, we need to do it in + * an idle as after the document is loaded the textview is still + * redrawing and relocating its internals. + */ + if (data->tab->idle_scroll == 0) + { + data->tab->idle_scroll = g_idle_add ((GSourceFunc)scroll_to_cursor, data->tab); + } + + location = gtk_source_file_loader_get_location (data->loader); + + /* If the document is readonly we don't care how many times the file + * is opened. + */ + if (!gtk_source_file_is_readonly (file) && + file_already_opened (doc, location)) + { +- TeplInfoBar *info_bar; ++ GtkWidget *info_bar; + + set_editable (data->tab, FALSE); + +- info_bar = tepl_io_error_info_bar_file_already_open (location); ++ info_bar = gedit_file_already_open_warning_info_bar_new (location); + + g_signal_connect (info_bar, + "response", + G_CALLBACK (file_already_open_warning_info_bar_response), + data->tab); + +- set_info_bar (data->tab, GTK_WIDGET (info_bar), GTK_RESPONSE_CANCEL); ++ set_info_bar (data->tab, info_bar, GTK_RESPONSE_CANCEL); + } + + /* When loading from stdin, the contents may not be saved, so set the + * buffer as modified. + */ + if (location == NULL) + { + gtk_text_buffer_set_modified (GTK_TEXT_BUFFER (doc), TRUE); + } + + data->tab->ask_if_externally_modified = TRUE; + + g_signal_emit_by_name (doc, "loaded"); + } + + static void + load_cb (GtkSourceFileLoader *loader, + GAsyncResult *result, + GTask *loading_task) + { + LoaderData *data = g_task_get_task_data (loading_task); + GeditDocument *doc; + GFile *location = gtk_source_file_loader_get_location (loader); + gboolean create_named_new_doc; + GError *error = NULL; + + g_clear_pointer (&data->timer, g_timer_destroy); + + gtk_source_file_loader_load_finish (loader, result, &error); + +@@ -2283,75 +2278,75 @@ save_cb (GtkSourceFileSaver *saver, + if (data->timer != NULL) + { + g_timer_destroy (data->timer); + data->timer = NULL; + } + + set_info_bar (tab, NULL, GTK_RESPONSE_NONE); + + if (error != NULL) + { + GtkWidget *info_bar; + + gedit_tab_set_state (tab, GEDIT_TAB_STATE_SAVING_ERROR); + + if (error->domain == GTK_SOURCE_FILE_SAVER_ERROR && + error->code == GTK_SOURCE_FILE_SAVER_ERROR_EXTERNALLY_MODIFIED) + { + /* This error is recoverable */ + info_bar = gedit_externally_modified_saving_error_info_bar_new (location, error); + g_return_if_fail (info_bar != NULL); + + g_signal_connect (info_bar, + "response", + G_CALLBACK (externally_modified_error_info_bar_response), + saving_task); + } + else if (error->domain == G_IO_ERROR && + error->code == G_IO_ERROR_CANT_CREATE_BACKUP) + { + /* This error is recoverable */ +- info_bar = GTK_WIDGET (tepl_io_error_info_bar_cant_create_backup (location, error)); ++ info_bar = gedit_no_backup_saving_error_info_bar_new (location, error); + g_return_if_fail (info_bar != NULL); + + g_signal_connect (info_bar, + "response", +- G_CALLBACK (cant_create_backup_error_info_bar_response), ++ G_CALLBACK (no_backup_error_info_bar_response), + saving_task); + } + else if (error->domain == GTK_SOURCE_FILE_SAVER_ERROR && + error->code == GTK_SOURCE_FILE_SAVER_ERROR_INVALID_CHARS) + { + /* If we have any invalid char in the document we must warn the user + * as it can make the document useless if it is saved. + */ +- info_bar = GTK_WIDGET (tepl_io_error_info_bar_invalid_characters (location)); ++ info_bar = gedit_invalid_character_info_bar_new (location); + g_return_if_fail (info_bar != NULL); + + g_signal_connect (info_bar, + "response", + G_CALLBACK (invalid_character_info_bar_response), + saving_task); + } + else if (error->domain == GTK_SOURCE_FILE_SAVER_ERROR || + (error->domain == G_IO_ERROR && + error->code != G_IO_ERROR_INVALID_DATA && + error->code != G_IO_ERROR_PARTIAL_INPUT)) + { + /* These errors are _NOT_ recoverable */ + gedit_recent_remove_if_local (location); + + info_bar = gedit_unrecoverable_saving_error_info_bar_new (location, error); + g_return_if_fail (info_bar != NULL); + + g_signal_connect (info_bar, + "response", + G_CALLBACK (unrecoverable_saving_error_info_bar_response), + saving_task); + } + else + { + const GtkSourceEncoding *encoding; + + /* This error is recoverable */ + g_return_if_fail (error->domain == G_CONVERT_ERROR || + error->domain == G_IO_ERROR); +@@ -2679,69 +2674,69 @@ get_print_settings (GeditTab *tab) + GEDIT_PRINT_SETTINGS_KEY); + + if (data == NULL) + { + settings = _gedit_app_get_default_print_settings (GEDIT_APP (g_application_get_default ())); + } + else + { + settings = gtk_print_settings_copy (GTK_PRINT_SETTINGS (data)); + } + + /* Be sure the OUTPUT_URI is unset, because otherwise the + * OUTPUT_BASENAME is not taken into account. + */ + gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_OUTPUT_URI, NULL); + + name = gedit_document_get_short_name_for_display (doc); + gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_OUTPUT_BASENAME, name); + + g_free (name); + + return settings; + } + + /* FIXME: show the info bar only if the operation will be "long" */ + static void + printing_cb (GeditPrintJob *job, + GeditPrintJobStatus status, + GeditTab *tab) + { +- g_return_if_fail (TEPL_IS_PROGRESS_INFO_BAR (tab->info_bar)); ++ g_return_if_fail (GEDIT_IS_PROGRESS_INFO_BAR (tab->info_bar)); + + gtk_widget_show (tab->info_bar); + +- tepl_progress_info_bar_set_text (TEPL_PROGRESS_INFO_BAR (tab->info_bar), +- gedit_print_job_get_status_string (job)); ++ gedit_progress_info_bar_set_text (GEDIT_PROGRESS_INFO_BAR (tab->info_bar), ++ gedit_print_job_get_status_string (job)); + +- tepl_progress_info_bar_set_fraction (TEPL_PROGRESS_INFO_BAR (tab->info_bar), +- gedit_print_job_get_progress (job)); ++ gedit_progress_info_bar_set_fraction (GEDIT_PROGRESS_INFO_BAR (tab->info_bar), ++ gedit_print_job_get_progress (job)); + } + + static void + store_print_settings (GeditTab *tab, + GeditPrintJob *job) + { + GeditDocument *doc; + GtkPrintSettings *settings; + GtkPageSetup *page_setup; + + doc = gedit_tab_get_document (tab); + + settings = gedit_print_job_get_print_settings (job); + + /* clear n-copies settings since we do not want to + * persist that one */ + gtk_print_settings_unset (settings, + GTK_PRINT_SETTINGS_N_COPIES); + + /* remember settings for this document */ + g_object_set_data_full (G_OBJECT (doc), + GEDIT_PRINT_SETTINGS_KEY, + g_object_ref (settings), + (GDestroyNotify)g_object_unref); + + /* make them the default */ + _gedit_app_set_default_print_settings (GEDIT_APP (g_application_get_default ()), + settings); + + page_setup = gedit_print_job_get_page_setup (job); +@@ -2797,73 +2792,75 @@ show_preview_cb (GeditPrintJob *job, + /* destroy the info bar */ + set_info_bar (tab, NULL, GTK_RESPONSE_NONE); + + tab->print_preview = GTK_WIDGET (preview); + g_object_ref_sink (tab->print_preview); + + gtk_box_pack_end (GTK_BOX (tab), tab->print_preview, TRUE, TRUE, 0); + + gtk_widget_show (tab->print_preview); + gtk_widget_grab_focus (tab->print_preview); + + gedit_tab_set_state (tab, GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW); + } + + static void + print_cancelled (GtkWidget *bar, + gint response_id, + GeditTab *tab) + { + gedit_debug (DEBUG_TAB); + + if (tab->print_job != NULL) + { + gedit_print_job_cancel (tab->print_job); + } + } + + static void + add_printing_info_bar (GeditTab *tab) + { +- TeplProgressInfoBar *bar; ++ GtkWidget *bar; + +- bar = tepl_progress_info_bar_new ("document-print", NULL, TRUE); ++ bar = gedit_progress_info_bar_new ("document-print", ++ "", ++ TRUE); + + g_signal_connect (bar, + "response", + G_CALLBACK (print_cancelled), + tab); + +- set_info_bar (tab, GTK_WIDGET (bar), GTK_RESPONSE_NONE); ++ set_info_bar (tab, bar, GTK_RESPONSE_NONE); + + /* hide until we start printing */ +- gtk_widget_hide (GTK_WIDGET (bar)); ++ gtk_widget_hide (bar); + } + + void + _gedit_tab_print (GeditTab *tab) + { + GeditView *view; + GtkPageSetup *setup; + GtkPrintSettings *settings; + GtkPrintOperationResult res; + GError *error = NULL; + + g_return_if_fail (GEDIT_IS_TAB (tab)); + + /* FIXME: currently we can have just one printoperation going on at a + * given time, so before starting the print we close the preview. + * Would be nice to handle it properly though. + */ + if (tab->state == GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW) + { + close_printing (tab); + } + + g_return_if_fail (tab->print_job == NULL); + g_return_if_fail (tab->state == GEDIT_TAB_STATE_NORMAL); + + view = gedit_tab_get_view (tab); + + tab->print_job = gedit_print_job_new (view); + + add_printing_info_bar (tab); +diff --git a/gedit/gedit-utils.c b/gedit/gedit-utils.c +index 9fc9e4fb0..ae7e92156 100644 +--- a/gedit/gedit-utils.c ++++ b/gedit/gedit-utils.c +@@ -1,56 +1,55 @@ + /* + * gedit-utils.c + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2002 Chema Celorio, Paolo Maggi + * Copyright (C) 2003-2005 Paolo Maggi + * + * 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, see . + */ + + #include "gedit-utils.h" + #include + #include +-#include + #include "gedit-debug.h" + + gboolean + gedit_utils_menu_position_under_tree_view (GtkTreeView *tree_view, + GdkRectangle *rect) + { + GtkTreeSelection *selection; + GtkTreeModel *model; + gint count_rows; + GList *rows; + gint widget_x, widget_y; + + model = gtk_tree_view_get_model (tree_view); + g_return_val_if_fail (model != NULL, FALSE); + + selection = gtk_tree_view_get_selection (tree_view); + g_return_val_if_fail (selection != NULL, FALSE); + + count_rows = gtk_tree_selection_count_selected_rows (selection); + if (count_rows != 1) + return FALSE; + + rows = gtk_tree_selection_get_selected_rows (selection, &model); + gtk_tree_view_get_cell_area (tree_view, (GtkTreePath *)(rows->data), + gtk_tree_view_get_column (tree_view, 0), + rect); + + gtk_tree_view_convert_bin_window_to_widget_coords (tree_view, rect->x, rect->y, &widget_x, &widget_y); + rect->x = widget_x; + rect->y = widget_y; +@@ -60,160 +59,321 @@ gedit_utils_menu_position_under_tree_view (GtkTreeView *tree_view, + } + + /** + * gedit_utils_set_atk_name_description: + * @widget: The Gtk widget for which name/description to be set + * @name: Atk name string + * @description: Atk description string + * + * This function sets up name and description + * for a specified gtk widget. + */ + void + gedit_utils_set_atk_name_description (GtkWidget *widget, + const gchar *name, + const gchar *description) + { + AtkObject *aobj; + + aobj = gtk_widget_get_accessible (widget); + + if (!(GTK_IS_ACCESSIBLE (aobj))) + return; + + if (name) + atk_object_set_name (aobj, name); + + if (description) + atk_object_set_description (aobj, description); + } + ++void ++gedit_warning (GtkWindow *parent, const gchar *format, ...) ++{ ++ va_list args; ++ gchar *str; ++ GtkWidget *dialog; ++ GtkWindowGroup *wg = NULL; ++ ++ g_return_if_fail (format != NULL); ++ ++ if (parent != NULL) ++ wg = gtk_window_get_group (parent); ++ ++ va_start (args, format); ++ str = g_strdup_vprintf (format, args); ++ va_end (args); ++ ++ dialog = gtk_message_dialog_new_with_markup ( ++ parent, ++ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, ++ GTK_MESSAGE_ERROR, ++ GTK_BUTTONS_OK, ++ "%s", str); ++ ++ g_free (str); ++ ++ if (wg != NULL) ++ gtk_window_group_add_window (wg, GTK_WINDOW (dialog)); ++ ++ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); ++ ++ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); ++ ++ g_signal_connect (G_OBJECT (dialog), ++ "response", ++ G_CALLBACK (gtk_widget_destroy), ++ NULL); ++ ++ gtk_widget_show (dialog); ++} ++ ++/* the following functions are taken from eel */ ++ ++static gchar * ++gedit_utils_str_truncate (const gchar *string, ++ guint truncate_length, ++ gboolean middle) ++{ ++ GString *truncated; ++ guint length; ++ guint n_chars; ++ guint num_left_chars; ++ guint right_offset; ++ guint delimiter_length; ++ const gchar *delimiter = "\342\200\246"; ++ ++ g_return_val_if_fail (string != NULL, NULL); ++ ++ length = strlen (string); ++ ++ g_return_val_if_fail (g_utf8_validate (string, length, NULL), NULL); ++ ++ /* It doesnt make sense to truncate strings to less than ++ * the size of the delimiter plus 2 characters (one on each ++ * side) ++ */ ++ delimiter_length = g_utf8_strlen (delimiter, -1); ++ if (truncate_length < (delimiter_length + 2)) ++ { ++ return g_strdup (string); ++ } ++ ++ n_chars = g_utf8_strlen (string, length); ++ ++ /* Make sure the string is not already small enough. */ ++ if (n_chars <= truncate_length) ++ { ++ return g_strdup (string); ++ } ++ ++ /* Find the 'middle' where the truncation will occur. */ ++ if (middle) ++ { ++ num_left_chars = (truncate_length - delimiter_length) / 2; ++ right_offset = n_chars - truncate_length + num_left_chars + delimiter_length; ++ ++ truncated = g_string_new_len (string, ++ g_utf8_offset_to_pointer (string, num_left_chars) - string); ++ g_string_append (truncated, delimiter); ++ g_string_append (truncated, g_utf8_offset_to_pointer (string, right_offset)); ++ } ++ else ++ { ++ num_left_chars = truncate_length - delimiter_length; ++ truncated = g_string_new_len (string, ++ g_utf8_offset_to_pointer (string, num_left_chars) - string); ++ g_string_append (truncated, delimiter); ++ } ++ ++ return g_string_free (truncated, FALSE); ++} ++ ++gchar * ++gedit_utils_str_middle_truncate (const gchar *string, ++ guint truncate_length) ++{ ++ return gedit_utils_str_truncate (string, truncate_length, TRUE); ++} ++ ++gchar * ++gedit_utils_str_end_truncate (const gchar *string, ++ guint truncate_length) ++{ ++ return gedit_utils_str_truncate (string, truncate_length, FALSE); ++} ++ + static gchar * + uri_get_dirname (const gchar *uri) + { + gchar *res; + gchar *str; + + g_return_val_if_fail (uri != NULL, NULL); + + /* CHECK: does it work with uri chaining? - Paolo */ + str = g_path_get_dirname (uri); + g_return_val_if_fail (str != NULL, g_strdup (".")); + + if ((strlen (str) == 1) && (*str == '.')) + { + g_free (str); + + return NULL; + } + +- res = tepl_utils_replace_home_dir_with_tilde (str); ++ res = gedit_utils_replace_home_dir_with_tilde (str); + + g_free (str); + + return res; + } + + /** + * gedit_utils_location_get_dirname_for_display: + * @location: the location + * + * Returns a string suitable to be displayed in the UI indicating + * the name of the directory where the file is located. + * For remote files it may also contain the hostname etc. + * For local files it tries to replace the home dir with ~. + * + * Returns: (transfer full): a string to display the dirname + */ + gchar * + gedit_utils_location_get_dirname_for_display (GFile *location) + { + gchar *uri; + gchar *res; + GMount *mount; + + g_return_val_if_fail (location != NULL, NULL); + + /* we use the parse name, that is either the local path + * or an uri but which is utf8 safe */ + uri = g_file_get_parse_name (location); + + /* FIXME: this is sync... is it a problem? */ + mount = g_file_find_enclosing_mount (location, NULL, NULL); + if (mount != NULL) + { + gchar *mount_name; + gchar *path = NULL; + gchar *dirname; + + mount_name = g_mount_get_name (mount); + g_object_unref (mount); + + /* obtain the "path" part of the uri */ +- tepl_utils_decode_uri (uri, +- NULL, NULL, +- NULL, NULL, +- &path); ++ gedit_utils_decode_uri (uri, ++ NULL, NULL, ++ NULL, NULL, ++ &path); + + if (path == NULL) + { + dirname = uri_get_dirname (uri); + } + else + { + dirname = uri_get_dirname (path); + } + + if (dirname == NULL || strcmp (dirname, ".") == 0) + { + res = mount_name; + } + else + { + res = g_strdup_printf ("%s %s", mount_name, dirname); + g_free (mount_name); + } + + g_free (path); + g_free (dirname); + } + else + { + /* fallback for local files or uris without mounts */ + res = uri_get_dirname (uri); + } + + g_free (uri); + + return res; + } + ++gchar * ++gedit_utils_replace_home_dir_with_tilde (const gchar *uri) ++{ ++ gchar *tmp; ++ gchar *home; ++ ++ g_return_val_if_fail (uri != NULL, NULL); ++ ++ /* Note that g_get_home_dir returns a const string */ ++ tmp = (gchar *)g_get_home_dir (); ++ ++ if (tmp == NULL) ++ return g_strdup (uri); ++ ++ home = g_filename_to_utf8 (tmp, -1, NULL, NULL, NULL); ++ if (home == NULL) ++ return g_strdup (uri); ++ ++ if (strcmp (uri, home) == 0) ++ { ++ g_free (home); ++ ++ return g_strdup ("~/"); ++ } ++ ++ tmp = home; ++ home = g_strdup_printf ("%s/", tmp); ++ g_free (tmp); ++ ++ if (g_str_has_prefix (uri, home)) ++ { ++ gchar *res; ++ ++ res = g_strdup_printf ("~/%s", uri + strlen (home)); ++ ++ g_free (home); ++ ++ return res; ++ } ++ ++ g_free (home); ++ ++ return g_strdup (uri); ++} ++ + static gboolean + is_valid_scheme_character (gchar c) + { + return g_ascii_isalnum (c) || c == '+' || c == '-' || c == '.'; + } + + static gboolean + has_valid_scheme (const gchar *uri) + { + const gchar *p; + + p = uri; + + if (!is_valid_scheme_character (*p)) + { + return FALSE; + } + + do + { + p++; + } while (is_valid_scheme_character (*p)); + + return *p == ':'; + } + + gboolean + gedit_utils_is_valid_location (GFile *location) + { + const guchar *p; +@@ -326,61 +486,61 @@ gedit_utils_basename_for_display (GFile *location) + + /* First, try to query the display name, but only on local files */ + if (g_file_has_uri_scheme (location, "file")) + { + GFileInfo *info; + + info = g_file_query_info (location, + G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, + G_FILE_QUERY_INFO_NONE, + NULL, + NULL); + + if (info) + { + /* Simply get the display name to use as the basename */ + name = g_strdup (g_file_info_get_display_name (info)); + g_object_unref (info); + } + else + { + /* This is a local file, and therefore we will use + * g_filename_display_basename on the local path */ + gchar *local_path; + + local_path = g_file_get_path (location); + name = g_filename_display_basename (local_path); + g_free (local_path); + } + } + else if (g_file_has_parent (location, NULL) || +- !tepl_utils_decode_uri (uri, NULL, NULL, &hn, NULL, NULL)) ++ !gedit_utils_decode_uri (uri, NULL, NULL, &hn, NULL, NULL)) + { + /* For remote files with a parent (so not just http://foo.com) + or remote file for which the decoding of the host name fails, + use the _parse_name and take basename of that */ + gchar *parse_name; + gchar *base; + + parse_name = g_file_get_parse_name (location); + base = g_filename_display_basename (parse_name); + name = g_uri_unescape_string (base, NULL); + + g_free (base); + g_free (parse_name); + } + else + { + /* display '/ on ' using the decoded host */ + gchar *hn_utf8; + + if (hn != NULL) + { + hn_utf8 = g_utf8_make_valid (hn, -1); + } + else + { + /* we should never get here */ + hn_utf8 = g_strdup ("?"); + } + + /* Translators: '/ on ' */ +@@ -411,60 +571,219 @@ gedit_utils_drop_get_uris (GtkSelectionData *selection_data) + gchar **uris; + gint i; + gint p = 0; + gchar **uri_list; + + uris = g_uri_list_extract_uris ((gchar *) gtk_selection_data_get_data (selection_data)); + uri_list = g_new0(gchar *, g_strv_length (uris) + 1); + + for (i = 0; uris[i] != NULL; i++) + { + gchar *uri; + + uri = make_canonical_uri_from_shell_arg (uris[i]); + + /* Silently ignore malformed URI/filename */ + if (uri != NULL) + uri_list[p++] = uri; + } + + if (*uri_list == NULL) + { + g_free(uri_list); + g_strfreev (uris); + return NULL; + } + + g_strfreev (uris); + return uri_list; + } + ++static void ++null_ptr (gchar **ptr) ++{ ++ if (ptr) ++ *ptr = NULL; ++} ++ ++/** ++ * gedit_utils_decode_uri: ++ * @uri: the uri to decode ++ * @scheme: (out) (allow-none): return value pointer for the uri's ++ * scheme (e.g. http, sftp, ...), or %NULL ++ * @user: (out) (allow-none): return value pointer for the uri user info, or %NULL ++ * @port: (out) (allow-none): return value pointer for the uri port, or %NULL ++ * @host: (out) (allow-none): return value pointer for the uri host, or %NULL ++ * @path: (out) (allow-none): return value pointer for the uri path, or %NULL ++ * ++ * Parse and break an uri apart in its individual components like the uri ++ * scheme, user info, port, host and path. The return value pointer can be ++ * %NULL to ignore certain parts of the uri. If the function returns %TRUE, then ++ * all return value pointers should be freed using g_free ++ * ++ * Return value: %TRUE if the uri could be properly decoded, %FALSE otherwise. ++ */ ++gboolean ++gedit_utils_decode_uri (const gchar *uri, ++ gchar **scheme, ++ gchar **user, ++ gchar **host, ++ gchar **port, ++ gchar **path) ++{ ++ /* Largely copied from glib/gio/gdummyfile.c:_g_decode_uri. This ++ * functionality should be in glib/gio, but for now we implement it ++ * ourselves (see bug #546182) */ ++ ++ const char *p, *in, *hier_part_start, *hier_part_end; ++ char *out; ++ char c; ++ ++ /* From RFC 3986 Decodes: ++ * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] ++ */ ++ ++ p = uri; ++ ++ null_ptr (scheme); ++ null_ptr (user); ++ null_ptr (port); ++ null_ptr (host); ++ null_ptr (path); ++ ++ /* Decode scheme: ++ * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) ++ */ ++ ++ if (!g_ascii_isalpha (*p)) ++ return FALSE; ++ ++ while (1) ++ { ++ c = *p++; ++ ++ if (c == ':') ++ break; ++ ++ if (!(g_ascii_isalnum(c) || ++ c == '+' || ++ c == '-' || ++ c == '.')) ++ { ++ return FALSE; ++ } ++ } ++ ++ if (scheme) ++ { ++ *scheme = g_malloc (p - uri); ++ out = *scheme; ++ ++ for (in = uri; in < p - 1; in++) ++ { ++ *out++ = g_ascii_tolower (*in); ++ } ++ ++ *out = '\0'; ++ } ++ ++ hier_part_start = p; ++ hier_part_end = p + strlen (p); ++ ++ if (hier_part_start[0] == '/' && hier_part_start[1] == '/') ++ { ++ const char *authority_start, *authority_end; ++ const char *userinfo_start, *userinfo_end; ++ const char *host_start, *host_end; ++ const char *port_start; ++ ++ authority_start = hier_part_start + 2; ++ /* authority is always followed by / or nothing */ ++ authority_end = memchr (authority_start, '/', hier_part_end - authority_start); ++ ++ if (authority_end == NULL) ++ authority_end = hier_part_end; ++ ++ /* 3.2: ++ * authority = [ userinfo "@" ] host [ ":" port ] ++ */ ++ ++ userinfo_end = memchr (authority_start, '@', authority_end - authority_start); ++ ++ if (userinfo_end) ++ { ++ userinfo_start = authority_start; ++ ++ if (user) ++ *user = g_uri_unescape_segment (userinfo_start, userinfo_end, NULL); ++ ++ if (user && *user == NULL) ++ { ++ if (scheme) ++ g_free (*scheme); ++ ++ return FALSE; ++ } ++ ++ host_start = userinfo_end + 1; ++ } ++ else ++ { ++ host_start = authority_start; ++ } ++ ++ port_start = memchr (host_start, ':', authority_end - host_start); ++ ++ if (port_start) ++ { ++ host_end = port_start++; ++ ++ if (port) ++ *port = g_strndup (port_start, authority_end - port_start); ++ } ++ else ++ { ++ host_end = authority_end; ++ } ++ ++ if (host) ++ *host = g_strndup (host_start, host_end - host_start); ++ ++ hier_part_start = authority_end; ++ } ++ ++ if (path) ++ *path = g_uri_unescape_segment (hier_part_start, hier_part_end, "/"); ++ ++ return TRUE; ++} ++ + GtkSourceCompressionType + gedit_utils_get_compression_type_from_content_type (const gchar *content_type) + { + if (content_type == NULL) + { + return GTK_SOURCE_COMPRESSION_TYPE_NONE; + } + + if (g_content_type_is_a (content_type, "application/x-gzip")) + { + return GTK_SOURCE_COMPRESSION_TYPE_GZIP; + } + + return GTK_SOURCE_COMPRESSION_TYPE_NONE; + } + + /* Copied from nautilus */ + static gchar * + get_direct_save_filename (GdkDragContext *context) + { + guchar *prop_text; + gint prop_len; + + if (!gdk_property_get (gdk_drag_context_get_source_window (context), gdk_atom_intern ("XdndDirectSave0", FALSE), + gdk_atom_intern ("text/plain", FALSE), 0, 1024, FALSE, NULL, NULL, + &prop_len, &prop_text) && prop_text != NULL) { + return NULL; + } + + /* Zero-terminate the string */ +diff --git a/gedit/gedit-utils.h b/gedit/gedit-utils.h +index a6b423db0..64af070d2 100644 +--- a/gedit/gedit-utils.h ++++ b/gedit/gedit-utils.h +@@ -1,56 +1,75 @@ + /* + * gedit-utils.h + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi + * Copyright (C) 2002 - 2005 Paolo Maggi + * + * 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, see . + */ + + #ifndef GEDIT_UTILS_H + #define GEDIT_UTILS_H + + #include + + G_BEGIN_DECLS + ++/* useful macro */ ++#define GBOOLEAN_TO_POINTER(i) (GINT_TO_POINTER ((i) ? 2 : 1)) ++#define GPOINTER_TO_BOOLEAN(i) ((gboolean) ((GPOINTER_TO_INT(i) == 2) ? TRUE : FALSE)) ++ + gboolean gedit_utils_menu_position_under_tree_view (GtkTreeView *tree_view, + GdkRectangle *rect); + ++gchar *gedit_utils_str_middle_truncate (const gchar *string, ++ guint truncate_length); ++gchar *gedit_utils_str_end_truncate (const gchar *string, ++ guint truncate_length); + void gedit_utils_set_atk_name_description (GtkWidget *widget, + const gchar *name, + const gchar *description); ++void gedit_warning (GtkWindow *parent, ++ const gchar *format, ++ ...) G_GNUC_PRINTF(2, 3); + +-gchar *gedit_utils_location_get_dirname_for_display (GFile *location); ++gchar *gedit_utils_location_get_dirname_for_display (GFile *location); ++gchar *gedit_utils_replace_home_dir_with_tilde (const gchar *uri); + + gboolean gedit_utils_is_valid_location (GFile *location); + + gchar *gedit_utils_basename_for_display (GFile *location); + ++gboolean gedit_utils_decode_uri (const gchar *uri, ++ gchar **scheme, ++ gchar **user, ++ gchar **host, ++ gchar **port, ++ gchar **path); ++ + /* Turns data from a drop into a list of well formatted uris */ + gchar **gedit_utils_drop_get_uris (GtkSelectionData *selection_data); + + gchar *gedit_utils_set_direct_save_filename (GdkDragContext *context); + + GtkSourceCompressionType gedit_utils_get_compression_type_from_content_type (const gchar *content_type); + + const gchar *gedit_utils_newline_type_to_string (GtkSourceNewlineType newline_type); + + G_END_DECLS + + #endif /* GEDIT_UTILS_H */ + + /* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-view-centering.c b/gedit/gedit-view-centering.c +new file mode 100644 +index 000000000..f9c742076 +--- /dev/null ++++ b/gedit/gedit-view-centering.c +@@ -0,0 +1,495 @@ ++/* ++ * gedit-view-centering.c ++ * This file is part of gedit ++ * ++ * Copyright (C) 2014 - Sébastien Lafargue ++ * ++ * Gedit is free software; you can redistribute this file and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * Gedit 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Based on Christian Hergert's prototype. ++ */ ++ ++#include "gedit-view-centering.h" ++ ++#include ++ ++#include "gedit-view.h" ++#include "gedit-debug.h" ++ ++struct _GeditViewCenteringPrivate ++{ ++ GtkWidget *box; ++ GtkWidget *scrolled_window; ++ GtkWidget *sourceview; ++ GtkWidget *spacer; ++ ++ GtkStyleContext *view_context; ++ GdkRGBA view_background; ++ GdkRGBA view_line_margin_fg; ++ GdkRGBA view_margin_background; ++ guint view_text_width; ++ ++ guint centered : 1; ++ guint view_background_set : 1; ++ guint view_line_margin_fg_set : 1; ++ guint view_margin_background_set : 1; ++}; ++ ++G_DEFINE_TYPE_WITH_PRIVATE (GeditViewCentering, gedit_view_centering, GTK_TYPE_BIN) ++ ++#define STYLE_TEXT "text" ++#define STYLE_RIGHT_MARGIN "right-margin" ++ ++#define RIGHT_MARGIN_LINE_ALPHA 40 ++#define RIGHT_MARGIN_OVERLAY_ALPHA 15 ++ ++static gboolean ++get_style (GtkSourceStyleScheme *scheme, ++ const gchar *style_id, ++ const gchar *attribute, ++ GdkRGBA *color) ++{ ++ GtkSourceStyle *style; ++ gchar *style_string; ++ ++ style = gtk_source_style_scheme_get_style (scheme, style_id); ++ if (!style) ++ { ++ return FALSE; ++ } ++ ++ g_object_get (style, attribute, &style_string, NULL); ++ if (style_string) ++ { ++ gdk_rgba_parse (color, style_string); ++ g_free (style_string); ++ ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++static void ++get_spacer_colors (GeditViewCentering *container, ++ GtkSourceStyleScheme *scheme) ++{ ++ GeditViewCenteringPrivate *priv = container->priv; ++ ++ if (scheme) ++ { ++ priv->view_background_set = get_style (scheme, ++ STYLE_TEXT, "background", ++ &priv->view_background); ++ ++ priv->view_line_margin_fg_set = get_style (scheme, ++ STYLE_RIGHT_MARGIN, "foreground", ++ &priv->view_line_margin_fg); ++ priv->view_line_margin_fg.alpha = RIGHT_MARGIN_LINE_ALPHA / 255.0; ++ ++ priv->view_margin_background_set = get_style (scheme, ++ STYLE_RIGHT_MARGIN, "background", ++ &priv->view_margin_background); ++ priv->view_margin_background.alpha = RIGHT_MARGIN_OVERLAY_ALPHA / 255.0; ++ } ++} ++ ++/* FIXME: when GeditViewCentering will be transfered to GtkSourceView, ++ * this method will be replaced by a call to a new method called ++ * gtk_source_view_get_right_margin_pixel_position () ++ */ ++static guint ++_gedit_view_centering_get_right_margin_pixel_position (GeditViewCentering *container) ++{ ++ GeditViewCenteringPrivate *priv; ++ gchar *str; ++ PangoFontDescription *font_desc; ++ PangoLayout *layout; ++ guint right_margin_position; ++ gint width = 0; ++ ++ g_return_val_if_fail (GEDIT_IS_VIEW_CENTERING (container), 0); ++ ++ priv = container->priv; ++ ++ right_margin_position = gtk_source_view_get_right_margin_position (GTK_SOURCE_VIEW (priv->sourceview)); ++ ++ gtk_style_context_save (priv->view_context); ++ gtk_style_context_set_state (priv->view_context, GTK_STATE_FLAG_NORMAL); ++ gtk_style_context_get (priv->view_context, ++ gtk_style_context_get_state (priv->view_context), ++ GTK_STYLE_PROPERTY_FONT, &font_desc, ++ NULL); ++ gtk_style_context_restore (priv->view_context); ++ ++ str = g_strnfill (right_margin_position, '_'); ++ layout = gtk_widget_create_pango_layout (GTK_WIDGET (priv->sourceview), str); ++ g_free (str); ++ ++ pango_layout_set_font_description (layout, font_desc); ++ pango_font_description_free (font_desc); ++ pango_layout_get_pixel_size (layout, &width, NULL); ++ ++ g_object_unref (G_OBJECT (layout)); ++ ++ return width; ++} ++ ++static void ++on_view_right_margin_visibility_changed (GeditView *view, ++ GParamSpec *pspec, ++ GeditViewCentering *container) ++{ ++ GeditViewCenteringPrivate *priv = container->priv; ++ gboolean visibility; ++ ++ visibility = gtk_source_view_get_show_right_margin (GTK_SOURCE_VIEW (priv->sourceview)); ++ ++ gtk_widget_set_visible (GTK_WIDGET (container->priv->spacer), visibility && priv->centered); ++} ++ ++static void ++on_view_right_margin_position_changed (GeditView *view, ++ GParamSpec *pspec, ++ GeditViewCentering *container) ++{ ++ GeditViewCenteringPrivate *priv = container->priv; ++ gboolean visibility; ++ ++ priv->view_text_width = _gedit_view_centering_get_right_margin_pixel_position (container); ++ ++ visibility = gtk_source_view_get_show_right_margin (GTK_SOURCE_VIEW (priv->sourceview)); ++ ++ if (visibility) ++ { ++ gtk_widget_queue_resize (priv->spacer); ++ } ++} ++ ++static void ++on_view_context_changed (GtkStyleContext *stylecontext, ++ GeditViewCentering *container) ++{ ++ GeditViewCenteringPrivate *priv = container->priv; ++ GtkTextBuffer *buffer; ++ GtkSourceStyleScheme *scheme; ++ gboolean visibility; ++ ++ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->sourceview)); ++ scheme = gtk_source_buffer_get_style_scheme (GTK_SOURCE_BUFFER (buffer)); ++ get_spacer_colors (container, scheme); ++ ++ priv->view_text_width = _gedit_view_centering_get_right_margin_pixel_position (container); ++ ++ visibility = gtk_source_view_get_show_right_margin (GTK_SOURCE_VIEW (priv->sourceview)); ++ ++ if (visibility) ++ { ++ gtk_widget_queue_resize (priv->spacer); ++ } ++} ++ ++static gboolean ++on_spacer_draw (GeditViewCentering *container, ++ cairo_t *cr, ++ GtkDrawingArea *spacer) ++{ ++ GeditViewCenteringPrivate *priv = container->priv; ++ GtkStyleContext *context; ++ guint width, height; ++ ++ if (!container->priv->sourceview) ++ { ++ return FALSE; ++ } ++ ++ width = gtk_widget_get_allocated_width (GTK_WIDGET (spacer)); ++ height = gtk_widget_get_allocated_height (GTK_WIDGET (spacer)); ++ ++ context = gtk_widget_get_style_context (GTK_WIDGET (spacer)); ++ gtk_style_context_save (context); ++ gtk_style_context_add_class (context, GTK_STYLE_CLASS_VIEW); ++ gtk_render_background (context, cr, 0, 0, width, height); ++ gtk_style_context_restore (context); ++ ++ cairo_set_line_width (cr, 1.0); ++ ++ if (priv->view_background_set) ++ { ++ gdk_cairo_set_source_rgba (cr, &container->priv->view_background); ++ cairo_rectangle (cr, 0, 0, width, height); ++ cairo_fill (cr); ++ } ++ ++ if (priv->view_margin_background_set) ++ { ++ gdk_cairo_set_source_rgba (cr, &container->priv->view_margin_background); ++ cairo_rectangle (cr, 0, 0, width, height); ++ cairo_fill (cr); ++ } ++ ++ if (priv->view_line_margin_fg_set) ++ { ++ gdk_cairo_set_source_rgba (cr, &container->priv->view_line_margin_fg); ++ cairo_move_to (cr, width - 0.5, 0); ++ cairo_line_to (cr, width - 0.5, height); ++ cairo_stroke (cr); ++ } ++ ++ return FALSE; ++} ++ ++static void ++gedit_view_centering_remove (GtkContainer *container, ++ GtkWidget *child) ++{ ++ GeditViewCenteringPrivate *priv; ++ ++ g_assert (GEDIT_IS_VIEW_CENTERING (container)); ++ ++ priv = GEDIT_VIEW_CENTERING (container)->priv; ++ ++ if (priv->sourceview == child) ++ { ++ gtk_container_remove (GTK_CONTAINER (priv->scrolled_window), priv->sourceview); ++ g_object_remove_weak_pointer (G_OBJECT (priv->sourceview), (gpointer *)&priv->sourceview); ++ priv->sourceview = NULL; ++ priv->view_context = NULL; ++ } ++ else ++ { ++ GTK_CONTAINER_CLASS (gedit_view_centering_parent_class)->remove (container, child); ++ } ++} ++ ++static void ++gedit_view_centering_add (GtkContainer *container, ++ GtkWidget *child) ++{ ++ GeditViewCenteringPrivate *priv; ++ GtkTextBuffer *buffer; ++ GtkSourceStyleScheme *scheme; ++ ++ g_assert (GEDIT_IS_VIEW_CENTERING (container)); ++ ++ priv = GEDIT_VIEW_CENTERING (container)->priv; ++ ++ if (GEDIT_IS_VIEW (child)) ++ { ++ if (priv->sourceview) ++ { ++ gedit_view_centering_remove (container, priv->sourceview); ++ } ++ ++ priv->sourceview = child; ++ g_object_add_weak_pointer (G_OBJECT (child), (gpointer *)&priv->sourceview); ++ gtk_container_add (GTK_CONTAINER (priv->scrolled_window), child); ++ ++ priv->view_context = gtk_widget_get_style_context (child); ++ ++ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->sourceview)); ++ scheme = gtk_source_buffer_get_style_scheme (GTK_SOURCE_BUFFER (buffer)); ++ get_spacer_colors (GEDIT_VIEW_CENTERING (container), scheme); ++ ++ g_signal_connect (priv->sourceview, ++ "notify::right-margin-position", ++ G_CALLBACK (on_view_right_margin_position_changed), ++ container); ++ ++ g_signal_connect (priv->sourceview, ++ "notify::show-right-margin", ++ G_CALLBACK (on_view_right_margin_visibility_changed), ++ container); ++ ++ g_signal_connect (priv->view_context, ++ "changed", ++ G_CALLBACK (on_view_context_changed), ++ container); ++ ++ gtk_widget_queue_resize (GTK_WIDGET (container)); ++ } ++ else ++ { ++ GTK_CONTAINER_CLASS (gedit_view_centering_parent_class)->add (container, child); ++ } ++} ++ ++static gboolean ++on_spacer_scroll_event (GtkWidget *widget, ++ GdkEvent *event, ++ GeditViewCentering *container) ++{ ++ GdkEventScroll *new_scroll_event; ++ ++ new_scroll_event = (GdkEventScroll *)gdk_event_copy (event); ++ g_object_unref (new_scroll_event->window); ++ ++ new_scroll_event->window = g_object_ref (gtk_widget_get_window (container->priv->sourceview)); ++ new_scroll_event->send_event = TRUE; ++ ++ new_scroll_event->x = 0; ++ new_scroll_event->y = 0; ++ new_scroll_event->x_root = 0; ++ new_scroll_event->y_root = 0; ++ ++ gtk_main_do_event ((GdkEvent *)new_scroll_event); ++ gdk_event_free ((GdkEvent *)new_scroll_event); ++ ++ return TRUE; ++} ++ ++static void ++gedit_view_centering_size_allocate (GtkWidget *widget, ++ GtkAllocation *alloc) ++{ ++ GeditViewCenteringPrivate *priv; ++ GtkTextView *view; ++ gint container_width; ++ gint gutter_width; ++ gint text_width; ++ gint spacer_width; ++ gint current_spacer_width; ++ GdkWindow *gutter_window; ++ ++ g_assert (GEDIT_IS_VIEW_CENTERING (widget)); ++ ++ priv = GEDIT_VIEW_CENTERING (widget)->priv; ++ ++ view = GTK_TEXT_VIEW (priv->sourceview); ++ ++ if (view) ++ { ++ container_width = alloc->width; ++ ++ gutter_window = gtk_text_view_get_window (view, GTK_TEXT_WINDOW_LEFT); ++ gutter_width = (gutter_window) ? gdk_window_get_width (gutter_window) : 0; ++ ++ text_width = priv->view_text_width; ++ spacer_width = MAX (0, container_width - text_width - gutter_width) / 2; ++ ++ g_object_get(priv->spacer, "width-request", ¤t_spacer_width, NULL); ++ ++ if (current_spacer_width != spacer_width) ++ { ++ g_object_set(priv->spacer, "width-request", spacer_width, NULL); ++ } ++ } ++ ++ GTK_WIDGET_CLASS (gedit_view_centering_parent_class)->size_allocate (widget, alloc); ++} ++ ++static void ++gedit_view_centering_finalize (GObject *object) ++{ ++ GeditViewCentering *container = GEDIT_VIEW_CENTERING (object); ++ GeditViewCenteringPrivate *priv = container->priv; ++ ++ if (priv->sourceview) ++ { ++ gedit_view_centering_remove (GTK_CONTAINER (container), priv->sourceview); ++ } ++ ++ G_OBJECT_CLASS (gedit_view_centering_parent_class)->finalize (object); ++} ++ ++static void ++gedit_view_centering_class_init (GeditViewCenteringClass *klass) ++{ ++ GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ++ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); ++ GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); ++ ++ gobject_class->finalize = gedit_view_centering_finalize; ++ ++ widget_class->size_allocate = gedit_view_centering_size_allocate; ++ ++ container_class->add = gedit_view_centering_add; ++ container_class->remove = gedit_view_centering_remove; ++} ++ ++static void ++gedit_view_centering_init (GeditViewCentering *container) ++{ ++ GeditViewCenteringPrivate *priv; ++ ++ container->priv = gedit_view_centering_get_instance_private (container); ++ priv = container->priv; ++ priv->view_text_width = 0; ++ ++ priv->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); ++ priv->spacer = gtk_drawing_area_new (); ++ priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL); ++ ++ gtk_container_add (GTK_CONTAINER (container), priv->box); ++ gtk_box_pack_start (GTK_BOX (priv->box), priv->spacer, FALSE, FALSE, 0); ++ gtk_box_pack_start (GTK_BOX (priv->box), priv->scrolled_window, TRUE, TRUE, 0); ++ ++ gtk_widget_set_no_show_all (GTK_WIDGET (priv->spacer), TRUE); ++ gtk_widget_show_all (GTK_WIDGET (priv->box)); ++ ++ g_signal_connect_swapped (priv->spacer, "draw", ++ G_CALLBACK (on_spacer_draw), ++ container); ++ ++ gtk_widget_add_events(GTK_WIDGET(priv->spacer), GDK_SCROLL_MASK); ++ g_signal_connect (priv->spacer, "scroll-event", ++ G_CALLBACK (on_spacer_scroll_event), ++ container); ++} ++ ++/** ++ * gedit_view_centering_set_centered: ++ * @container: a #GeditViewCentering. ++ * @centered: whether to center the sourceview child or not. ++ * ++ * If @centered is %TRUE, the sourceview child is centered ++ * horizontally on the #GeditViewCentering container. ++ **/ ++void ++gedit_view_centering_set_centered (GeditViewCentering *container, ++ gboolean centered) ++{ ++ g_return_if_fail (GEDIT_IS_VIEW_CENTERING (container)); ++ ++ container->priv->centered = centered != FALSE; ++ ++ on_view_right_margin_visibility_changed (GEDIT_VIEW (container->priv->sourceview), NULL, container); ++} ++ ++/** ++ * gedit_view_centering_get_centered: ++ * @container: a #GeditViewCentering. ++ * ++ * Return whether the #GtkSourceView child is centered or not. ++ * ++ * Return value: %TRUE if the #GtkSourceView child is centered ++ * horizontally on the #GeditViewCentering container. ++ **/ ++gboolean ++gedit_view_centering_get_centered (GeditViewCentering *container) ++{ ++ g_return_val_if_fail (GEDIT_IS_VIEW_CENTERING (container), FALSE); ++ ++ return container->priv->centered; ++} ++ ++GeditViewCentering * ++gedit_view_centering_new (void) ++{ ++ return g_object_new (GEDIT_TYPE_VIEW_CENTERING, ++ NULL); ++} ++ ++/* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-view-centering.h b/gedit/gedit-view-centering.h +new file mode 100644 +index 000000000..1d7218969 +--- /dev/null ++++ b/gedit/gedit-view-centering.h +@@ -0,0 +1,67 @@ ++/* ++ * gedit-view-centering.h ++ * This file is part of gedit ++ * ++ * Copyright (C) 2014 - Sébastien Lafargue ++ * Copyright (C) 2015 - Sébastien Wilmet ++ * ++ * Gedit is free software; you can redistribute this file and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * Gedit 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#ifndef GEDIT_VIEW_CENTERING_H ++#define GEDIT_VIEW_CENTERING_H ++ ++#include ++ ++G_BEGIN_DECLS ++ ++#define GEDIT_TYPE_VIEW_CENTERING (gedit_view_centering_get_type()) ++#define GEDIT_VIEW_CENTERING(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_VIEW_CENTERING, GeditViewCentering)) ++#define GEDIT_VIEW_CENTERING_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_VIEW_CENTERING, GeditViewCentering const)) ++#define GEDIT_VIEW_CENTERING_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_VIEW_CENTERING, GeditViewCenteringClass)) ++#define GEDIT_IS_VIEW_CENTERING(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_VIEW_CENTERING)) ++#define GEDIT_IS_VIEW_CENTERING_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_VIEW_CENTERING)) ++#define GEDIT_VIEW_CENTERING_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GEDIT_TYPE_VIEW_CENTERING, GeditViewCenteringClass)) ++ ++typedef struct _GeditViewCentering GeditViewCentering; ++typedef struct _GeditViewCenteringClass GeditViewCenteringClass; ++typedef struct _GeditViewCenteringPrivate GeditViewCenteringPrivate; ++ ++struct _GeditViewCentering ++{ ++ GtkBin parent; ++ ++ GeditViewCenteringPrivate *priv; ++}; ++ ++struct _GeditViewCenteringClass ++{ ++ GtkBinClass parent_class; ++}; ++ ++GType gedit_view_centering_get_type (void) G_GNUC_CONST; ++ ++GeditViewCentering * gedit_view_centering_new (void); ++ ++void gedit_view_centering_set_centered (GeditViewCentering *container, ++ gboolean centered); ++ ++gboolean gedit_view_centering_get_centered (GeditViewCentering *container); ++ ++G_END_DECLS ++ ++#endif /* GEDIT_VIEW_CENTERING_H */ ++ ++/* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-view-frame.c b/gedit/gedit-view-frame.c +index f41734c47..67c067a41 100644 +--- a/gedit/gedit-view-frame.c ++++ b/gedit/gedit-view-frame.c +@@ -1,86 +1,91 @@ + /* + * gedit-view-frame.c + * This file is part of gedit + * + * Copyright (C) 2010 - Ignacio Casal Quinteiro +- * Copyright (C) 2013, 2019 - Sébastien Wilmet ++ * Copyright (C) 2013 - Sébastien Wilmet + * + * gedit 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. + * + * gedit 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 gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + + #include "gedit-view-frame.h" + + #include + #include + #include + #include + ++#include "gedit-view-centering.h" + #include "gedit-debug.h" + #include "gedit-utils.h" + #include "gedit-settings.h" + #include "libgd/gd.h" + + #define FLUSH_TIMEOUT_DURATION 30 /* in seconds */ + + #define SEARCH_POPUP_MARGIN 12 + + typedef enum + { + GOTO_LINE, + SEARCH + } SearchMode; + + typedef enum + { + SEARCH_STATE_NORMAL, + SEARCH_STATE_NOT_FOUND + } SearchState; + + struct _GeditViewFrame + { + GtkOverlay parent_instance; + ++ GSettings *editor_settings; ++ + GeditView *view; ++ GeditViewCentering *view_centering; ++ GtkFrame *map_frame; + + SearchMode search_mode; + + /* Where the search has started. When the user presses escape in the + * search entry (to cancel the search), we return to the start_mark. + */ + GtkTextMark *start_mark; + + GtkRevealer *revealer; + GdTaggedEntry *search_entry; + GdTaggedEntryTag *entry_tag; + GtkWidget *go_up_button; + GtkWidget *go_down_button; + + guint flush_timeout_id; + guint idle_update_entry_tag_id; + guint remove_entry_tag_timeout_id; + gulong view_scroll_event_id; + gulong search_entry_focus_out_id; + gulong search_entry_changed_id; + + GtkSourceSearchSettings *search_settings; + + /* Used to restore the search state if an incremental search is + * cancelled. + */ + GtkSourceSearchSettings *old_search_settings; + + /* The original search texts. In search_settings and + * old_search_settings, the search text is unescaped. Since the escape +@@ -132,115 +137,116 @@ gedit_view_frame_dispose (GObject *object) + if (frame->start_mark != NULL && buffer != NULL) + { + gtk_text_buffer_delete_mark (buffer, frame->start_mark); + frame->start_mark = NULL; + } + + if (frame->flush_timeout_id != 0) + { + g_source_remove (frame->flush_timeout_id); + frame->flush_timeout_id = 0; + } + + if (frame->idle_update_entry_tag_id != 0) + { + g_source_remove (frame->idle_update_entry_tag_id); + frame->idle_update_entry_tag_id = 0; + } + + if (frame->remove_entry_tag_timeout_id != 0) + { + g_source_remove (frame->remove_entry_tag_timeout_id); + frame->remove_entry_tag_timeout_id = 0; + } + + if (buffer != NULL) + { + GtkSourceFile *file = gedit_document_get_file (GEDIT_DOCUMENT (buffer)); + gtk_source_file_set_mount_operation_factory (file, NULL, NULL, NULL); + } + ++ g_clear_object (&frame->editor_settings); + g_clear_object (&frame->entry_tag); + g_clear_object (&frame->search_settings); + g_clear_object (&frame->old_search_settings); + + G_OBJECT_CLASS (gedit_view_frame_parent_class)->dispose (object); + } + + static void + gedit_view_frame_finalize (GObject *object) + { + GeditViewFrame *frame = GEDIT_VIEW_FRAME (object); + + g_free (frame->search_text); + g_free (frame->old_search_text); + + G_OBJECT_CLASS (gedit_view_frame_parent_class)->finalize (object); + } + + static void + hide_search_widget (GeditViewFrame *frame, + gboolean cancel) + { + GtkTextBuffer *buffer; + + if (!gtk_revealer_get_reveal_child (frame->revealer)) + { + return; + } + + if (frame->view_scroll_event_id != 0) + { + g_signal_handler_disconnect (frame->view, + frame->view_scroll_event_id); + frame->view_scroll_event_id = 0; + } + + if (frame->flush_timeout_id != 0) + { + g_source_remove (frame->flush_timeout_id); + frame->flush_timeout_id = 0; + } + + gtk_revealer_set_reveal_child (frame->revealer, FALSE); + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (frame->view)); + + if (cancel && frame->start_mark != NULL) + { + GtkTextIter iter; + + gtk_text_buffer_get_iter_at_mark (buffer, &iter, + frame->start_mark); + gtk_text_buffer_place_cursor (buffer, &iter); + +- tepl_view_scroll_to_cursor (TEPL_VIEW (frame->view)); ++ gedit_view_scroll_to_cursor (frame->view); + } + + if (frame->start_mark != NULL) + { + gtk_text_buffer_delete_mark (buffer, frame->start_mark); + frame->start_mark = NULL; + } + } + + static gboolean + search_entry_flush_timeout (GeditViewFrame *frame) + { + frame->flush_timeout_id = 0; + hide_search_widget (frame, FALSE); + + return G_SOURCE_REMOVE; + } + + static void + renew_flush_timeout (GeditViewFrame *frame) + { + if (frame->flush_timeout_id != 0) + { + g_source_remove (frame->flush_timeout_id); + } + + frame->flush_timeout_id = + g_timeout_add_seconds (FLUSH_TIMEOUT_DURATION, + (GSourceFunc)search_entry_flush_timeout, + frame); +@@ -270,61 +276,61 @@ get_search_context (GeditViewFrame *frame) + + return NULL; + } + + static void + set_search_state (GeditViewFrame *frame, + SearchState state) + { + GtkStyleContext *context; + + context = gtk_widget_get_style_context (GTK_WIDGET (frame->search_entry)); + + if (state == SEARCH_STATE_NOT_FOUND) + { + gtk_style_context_add_class (context, GTK_STYLE_CLASS_ERROR); + } + else + { + gtk_style_context_remove_class (context, GTK_STYLE_CLASS_ERROR); + } + } + + static void + finish_search (GeditViewFrame *frame, + gboolean found) + { + const gchar *entry_text = gtk_entry_get_text (GTK_ENTRY (frame->search_entry)); + + if (found || (entry_text[0] == '\0')) + { +- tepl_view_scroll_to_cursor (TEPL_VIEW (frame->view)); ++ gedit_view_scroll_to_cursor (frame->view); + + set_search_state (frame, SEARCH_STATE_NORMAL); + } + else + { + set_search_state (frame, SEARCH_STATE_NOT_FOUND); + } + } + + static void + start_search_finished (GtkSourceSearchContext *search_context, + GAsyncResult *result, + GeditViewFrame *frame) + { + GtkTextIter match_start; + GtkTextIter match_end; + gboolean found; + GtkSourceBuffer *buffer; + + found = gtk_source_search_context_forward_finish (search_context, + result, + &match_start, + &match_end, + NULL, + NULL); + + buffer = gtk_source_search_context_get_buffer (search_context); + + if (found) + { +@@ -1034,117 +1040,121 @@ customize_for_search_mode (GeditViewFrame *frame) + gtk_widget_set_tooltip_text (GTK_WIDGET (frame->search_entry), + _("Line you want to move the cursor to")); + + gtk_widget_hide (frame->go_up_button); + gtk_widget_hide (frame->go_down_button); + } + + gtk_entry_set_icon_from_gicon (GTK_ENTRY (frame->search_entry), + GTK_ENTRY_ICON_PRIMARY, + icon); + + gtk_widget_set_size_request (GTK_WIDGET (frame->search_entry), + width_request, + -1); + + g_object_unref (icon); + } + + static void + update_goto_line (GeditViewFrame *frame) + { + const gchar *entry_text; + gboolean moved; + gboolean moved_offset; + gint line; + gint offset_line = 0; + gint line_offset = 0; + gchar **split_text = NULL; + const gchar *text; + GtkTextIter iter; ++ GeditDocument *doc; + + entry_text = gtk_entry_get_text (GTK_ENTRY (frame->search_entry)); + + if (entry_text[0] == '\0') + { + return; + } + + get_iter_at_start_mark (frame, &iter); + + split_text = g_strsplit (entry_text, ":", -1); + + if (g_strv_length (split_text) > 1) + { + text = split_text[0]; + } + else + { + text = entry_text; + } + + if (text[0] == '-') + { + gint cur_line = gtk_text_iter_get_line (&iter); + + if (text[1] != '\0') + { + offset_line = MAX (atoi (text + 1), 0); + } + + line = MAX (cur_line - offset_line, 0); + } + else if (entry_text[0] == '+') + { + gint cur_line = gtk_text_iter_get_line (&iter); + + if (text[1] != '\0') + { + offset_line = MAX (atoi (text + 1), 0); + } + + line = cur_line + offset_line; + } + else + { + line = MAX (atoi (text) - 1, 0); + } + + if (split_text[1] != NULL) + { + line_offset = atoi (split_text[1]); + } + + g_strfreev (split_text); + +- moved = tepl_view_goto_line (TEPL_VIEW (frame->view), line); +- moved_offset = tepl_view_goto_line_offset (TEPL_VIEW (frame->view), line, line_offset); ++ doc = get_document (frame); ++ moved = gedit_document_goto_line (doc, line); ++ moved_offset = gedit_document_goto_line_offset (doc, line, line_offset); ++ ++ gedit_view_scroll_to_cursor (frame->view); + + if (!moved || !moved_offset) + { + set_search_state (frame, SEARCH_STATE_NOT_FOUND); + } + else + { + set_search_state (frame, SEARCH_STATE_NORMAL); + } + } + + static void + search_entry_changed_cb (GtkEntry *entry, + GeditViewFrame *frame) + { + renew_flush_timeout (frame); + + if (frame->search_mode == SEARCH) + { + update_search_text (frame); + start_search (frame); + } + else + { + update_goto_line (frame); + } + } + + static gboolean + search_entry_focus_out_event (GtkWidget *widget, +@@ -1401,86 +1411,95 @@ start_interactive_search_real (GeditViewFrame *frame, + + gtk_widget_grab_focus (GTK_WIDGET (frame->search_entry)); + + customize_for_search_mode (frame); + init_search_entry (frame); + + /* Manage the scroll also for the view */ + frame->view_scroll_event_id = + g_signal_connect (frame->view, "scroll-event", + G_CALLBACK (search_widget_scroll_event), + frame); + + renew_flush_timeout (frame); + + install_update_entry_tag_idle (frame); + } + + static void + gedit_view_frame_class_init (GeditViewFrameClass *klass) + { + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = gedit_view_frame_dispose; + object_class->finalize = gedit_view_frame_finalize; + + /* Bind class to template */ + gtk_widget_class_set_template_from_resource (widget_class, + "/org/gnome/gedit/ui/gedit-view-frame.ui"); + gtk_widget_class_bind_template_child (widget_class, GeditViewFrame, view); ++ gtk_widget_class_bind_template_child (widget_class, GeditViewFrame, view_centering); ++ gtk_widget_class_bind_template_child (widget_class, GeditViewFrame, map_frame); + gtk_widget_class_bind_template_child (widget_class, GeditViewFrame, revealer); + gtk_widget_class_bind_template_child (widget_class, GeditViewFrame, search_entry); + gtk_widget_class_bind_template_child (widget_class, GeditViewFrame, go_up_button); + gtk_widget_class_bind_template_child (widget_class, GeditViewFrame, go_down_button); + } + + static GMountOperation * + view_frame_mount_operation_factory (GtkSourceFile *file, + gpointer user_data) + { + GtkWidget *view_frame = user_data; + GtkWidget *window = gtk_widget_get_toplevel (view_frame); + + return gtk_mount_operation_new (GTK_WINDOW (window)); + } + + static void + gedit_view_frame_init (GeditViewFrame *frame) + { + GeditDocument *doc; + GtkSourceFile *file; + + gedit_debug (DEBUG_WINDOW); + + gtk_widget_init_template (GTK_WIDGET (frame)); + ++ frame->editor_settings = g_settings_new ("org.gnome.gedit.preferences.editor"); ++ g_settings_bind (frame->editor_settings, ++ GEDIT_SETTINGS_DISPLAY_OVERVIEW_MAP, ++ frame->map_frame, ++ "visible", ++ G_SETTINGS_BIND_GET | G_SETTINGS_BIND_NO_SENSITIVITY); ++ + doc = get_document (frame); + file = gedit_document_get_file (doc); + + gtk_source_file_set_mount_operation_factory (file, + view_frame_mount_operation_factory, + frame, + NULL); + + frame->entry_tag = gd_tagged_entry_tag_new (""); + + gd_tagged_entry_tag_set_style (frame->entry_tag, + "gedit-search-entry-occurrences-tag"); + + gd_tagged_entry_tag_set_has_close_button (frame->entry_tag, FALSE); + + gtk_widget_set_margin_end (GTK_WIDGET (frame->revealer), + SEARCH_POPUP_MARGIN); + + g_signal_connect (doc, + "mark-set", + G_CALLBACK (mark_set_cb), + frame); + + g_signal_connect (frame->revealer, + "key-press-event", + G_CALLBACK (search_widget_key_press_event), + frame); + + g_signal_connect (frame->revealer, + "scroll-event", +@@ -1524,60 +1543,68 @@ gedit_view_frame_init (GeditViewFrame *frame) + + frame->search_entry_changed_id = + g_signal_connect (frame->search_entry, + "changed", + G_CALLBACK (search_entry_changed_cb), + frame); + + frame->search_entry_focus_out_id = + g_signal_connect (frame->search_entry, + "focus-out-event", + G_CALLBACK (search_entry_focus_out_event), + frame); + + g_signal_connect_swapped (frame->go_up_button, + "clicked", + G_CALLBACK (backward_search), + frame); + + g_signal_connect_swapped (frame->go_down_button, + "clicked", + G_CALLBACK (forward_search), + frame); + } + + GeditViewFrame * + gedit_view_frame_new (void) + { + return g_object_new (GEDIT_TYPE_VIEW_FRAME, NULL); + } + ++GeditViewCentering * ++gedit_view_frame_get_view_centering (GeditViewFrame *frame) ++{ ++ g_return_val_if_fail (GEDIT_IS_VIEW_FRAME (frame), NULL); ++ ++ return frame->view_centering; ++} ++ + GeditView * + gedit_view_frame_get_view (GeditViewFrame *frame) + { + g_return_val_if_fail (GEDIT_IS_VIEW_FRAME (frame), NULL); + + return frame->view; + } + + void + gedit_view_frame_popup_search (GeditViewFrame *frame) + { + g_return_if_fail (GEDIT_IS_VIEW_FRAME (frame)); + + start_interactive_search_real (frame, SEARCH); + } + + void + gedit_view_frame_popup_goto_line (GeditViewFrame *frame) + { + g_return_if_fail (GEDIT_IS_VIEW_FRAME (frame)); + + start_interactive_search_real (frame, GOTO_LINE); + } + + void + gedit_view_frame_clear_search (GeditViewFrame *frame) + { + g_return_if_fail (GEDIT_IS_VIEW_FRAME (frame)); + + g_signal_handler_block (frame->search_entry, +diff --git a/gedit/gedit-view-frame.h b/gedit/gedit-view-frame.h +index 78ac469d1..d2e550840 100644 +--- a/gedit/gedit-view-frame.h ++++ b/gedit/gedit-view-frame.h +@@ -1,45 +1,49 @@ + /* + * gedit-view-frame.h + * This file is part of gedit + * + * Copyright (C) 2010 - Ignacio Casal Quinteiro + * + * gedit 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. + * + * gedit 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 gedit. If not, see . + */ + + #ifndef GEDIT_VIEW_FRAME_H + #define GEDIT_VIEW_FRAME_H + + #include + #include "gedit-document.h" + #include "gedit-view.h" ++#include "gedit-view-centering.h" + + G_BEGIN_DECLS + + #define GEDIT_TYPE_VIEW_FRAME (gedit_view_frame_get_type ()) + G_DECLARE_FINAL_TYPE (GeditViewFrame, gedit_view_frame, GEDIT, VIEW_FRAME, GtkOverlay) + + GeditViewFrame *gedit_view_frame_new (void); + ++GeditViewCentering ++ *gedit_view_frame_get_view_centering (GeditViewFrame *frame); ++ + GeditView *gedit_view_frame_get_view (GeditViewFrame *frame); + + void gedit_view_frame_popup_search (GeditViewFrame *frame); + + void gedit_view_frame_popup_goto_line (GeditViewFrame *frame); + + void gedit_view_frame_clear_search (GeditViewFrame *frame); + + G_END_DECLS + + #endif /* GEDIT_VIEW_FRAME_H */ +diff --git a/gedit/gedit-view.c b/gedit/gedit-view.c +index 0c730c989..035e599ab 100644 +--- a/gedit/gedit-view.c ++++ b/gedit/gedit-view.c +@@ -1,232 +1,259 @@ + /* + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2002 Chema Celorio, Paolo Maggi + * Copyright (C) 2003-2005 Paolo Maggi + * + * 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, see . + */ + + #include "gedit-view.h" + #include + #include "gedit-view-activatable.h" + #include "gedit-plugins-engine.h" + #include "gedit-debug.h" ++#include "gedit-pango.h" + #include "gedit-utils.h" + #include "gedit-settings.h" + ++#define GEDIT_VIEW_SCROLL_MARGIN 0.02 ++ + struct _GeditViewPrivate + { ++ GeditDocument *current_document; + PeasExtensionSet *extensions; + + gchar *direct_save_uri; + +- TeplSignalGroup *file_signal_group; ++ GtkCssProvider *css_provider; ++ PangoFontDescription *font_desc; + }; + + enum + { + TARGET_URI_LIST = 100, + TARGET_XDNDDIRECTSAVE + }; + + enum + { + SIGNAL_DROP_URIS, + N_SIGNALS + }; + + static guint signals[N_SIGNALS]; + +-G_DEFINE_TYPE_WITH_PRIVATE (GeditView, gedit_view, TEPL_TYPE_VIEW) ++G_DEFINE_TYPE_WITH_PRIVATE (GeditView, gedit_view, GTK_SOURCE_TYPE_VIEW) + + static void + update_editable (GeditView *view) + { + GeditDocument *doc; + GtkSourceFile *file; + + doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))); + file = gedit_document_get_file (doc); + + gtk_text_view_set_editable (GTK_TEXT_VIEW (view), + !gtk_source_file_is_readonly (file)); + } + + static void + file_read_only_notify_cb (GtkSourceFile *file, + GParamSpec *pspec, + GeditView *view) + { + update_editable (view); + } + ++static void ++current_document_removed (GeditView *view) ++{ ++ if (view->priv->current_document != NULL) ++ { ++ GtkSourceFile *file; ++ ++ file = gedit_document_get_file (view->priv->current_document); ++ ++ g_signal_handlers_disconnect_by_func (file, ++ file_read_only_notify_cb, ++ view); ++ ++ g_object_unref (view->priv->current_document); ++ view->priv->current_document = NULL; ++ } ++} ++ + static void + buffer_changed (GeditView *view) + { +- GeditDocument *doc; + GtkSourceFile *file; ++ GtkTextBuffer *buffer; + +- doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))); +- file = gedit_document_get_file (doc); ++ current_document_removed (view); ++ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); ++ ++ if (!GEDIT_IS_DOCUMENT (buffer)) ++ { ++ return; ++ } + +- tepl_signal_group_clear (&view->priv->file_signal_group); +- view->priv->file_signal_group = tepl_signal_group_new (G_OBJECT (file)); ++ view->priv->current_document = g_object_ref (GEDIT_DOCUMENT (buffer)); + +- tepl_signal_group_add (view->priv->file_signal_group, +- g_signal_connect (file, +- "notify::read-only", +- G_CALLBACK (file_read_only_notify_cb), +- view)); ++ file = gedit_document_get_file (view->priv->current_document); ++ g_signal_connect_object (file, ++ "notify::read-only", ++ G_CALLBACK (file_read_only_notify_cb), ++ view, ++ 0); + + update_editable (view); + } + + static void + buffer_notify_cb (GeditView *view, + GParamSpec *pspec, + gpointer user_data) + { + buffer_changed (view); + } + + static void + gedit_view_init (GeditView *view) + { + GtkTargetList *target_list; +- GtkStyleContext *style_context; ++ GtkStyleContext *context; + + gedit_debug (DEBUG_VIEW); + + view->priv = gedit_view_get_instance_private (view); + + /* Drag and drop support */ + view->priv->direct_save_uri = NULL; + target_list = gtk_drag_dest_get_target_list (GTK_WIDGET (view)); + + if (target_list != NULL) + { + gtk_target_list_add (target_list, + gdk_atom_intern ("XdndDirectSave0", FALSE), + 0, + TARGET_XDNDDIRECTSAVE); + gtk_target_list_add_uri_targets (target_list, TARGET_URI_LIST); + } + + /* GeditViewActivatable */ + view->priv->extensions = + peas_extension_set_new (PEAS_ENGINE (gedit_plugins_engine_get_default ()), + GEDIT_TYPE_VIEW_ACTIVATABLE, + "view", view, + NULL); + + /* Act on buffer changes */ + buffer_changed (view); + g_signal_connect (view, + "notify::buffer", + G_CALLBACK (buffer_notify_cb), + NULL); + + /* CSS stuff */ +- style_context = gtk_widget_get_style_context (GTK_WIDGET (view)); +- gtk_style_context_add_class (style_context, "gedit-view"); ++ context = gtk_widget_get_style_context (GTK_WIDGET (view)); ++ gtk_style_context_add_class (context, "gedit-view"); ++ ++ view->priv->css_provider = gtk_css_provider_new (); ++ gtk_style_context_add_provider (context, ++ GTK_STYLE_PROVIDER (view->priv->css_provider), ++ GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + } + + static void + gedit_view_dispose (GObject *object) + { + GeditView *view = GEDIT_VIEW (object); + + g_clear_object (&view->priv->extensions); +- tepl_signal_group_clear (&view->priv->file_signal_group); ++ ++ current_document_removed (view); + + /* Disconnect notify buffer because the destroy of the textview will set + * the buffer to NULL, and we call get_buffer in the notify which would + * reinstate a buffer which we don't want. + * There is no problem calling g_signal_handlers_disconnect_by_func() + * several times (if dispose() is called several times). + */ + g_signal_handlers_disconnect_by_func (view, buffer_notify_cb, NULL); + +- G_OBJECT_CLASS (gedit_view_parent_class)->dispose (object); +-} +- +-static void +-update_font (GeditView *view) +-{ +- GeditSettings *settings; +- gchar *selected_font; +- +- settings = _gedit_settings_get_singleton (); +- selected_font = _gedit_settings_get_selected_font (settings); +- tepl_utils_override_font (GTK_WIDGET (view), selected_font); +- g_free (selected_font); +-} ++ g_clear_object (&view->priv->css_provider); ++ g_clear_pointer (&view->priv->font_desc, pango_font_description_free); + +-static void +-fonts_changed_cb (GeditSettings *settings, +- GeditView *view) +-{ +- update_font (view); ++ G_OBJECT_CLASS (gedit_view_parent_class)->dispose (object); + } + + static void + gedit_view_constructed (GObject *object) + { + GeditView *view = GEDIT_VIEW (object); + GeditSettings *settings; + GSettings *editor_settings; ++ gboolean use_default_font; + + G_OBJECT_CLASS (gedit_view_parent_class)->constructed (object); + + settings = _gedit_settings_get_singleton (); + editor_settings = _gedit_settings_peek_editor_settings (settings); + +- update_font (view); +- g_signal_connect_object (settings, +- "fonts-changed", +- G_CALLBACK (fonts_changed_cb), +- view, +- 0); ++ use_default_font = g_settings_get_boolean (editor_settings, GEDIT_SETTINGS_USE_DEFAULT_FONT); ++ ++ if (use_default_font) ++ { ++ gedit_view_set_font (view, TRUE, NULL); ++ } ++ else ++ { ++ gchar *editor_font; ++ ++ editor_font = g_settings_get_string (editor_settings, GEDIT_SETTINGS_EDITOR_FONT); ++ gedit_view_set_font (view, FALSE, editor_font); ++ g_free (editor_font); ++ } + + g_settings_bind (editor_settings, GEDIT_SETTINGS_DISPLAY_LINE_NUMBERS, + view, "show-line-numbers", + G_SETTINGS_BIND_GET); + + g_settings_bind (editor_settings, GEDIT_SETTINGS_AUTO_INDENT, + view, "auto-indent", + G_SETTINGS_BIND_GET); + + g_settings_bind (editor_settings, GEDIT_SETTINGS_TABS_SIZE, + view, "tab-width", + G_SETTINGS_BIND_GET); + + g_settings_bind (editor_settings, GEDIT_SETTINGS_INSERT_SPACES, + view, "insert-spaces-instead-of-tabs", + G_SETTINGS_BIND_GET); + + g_settings_bind (editor_settings, GEDIT_SETTINGS_DISPLAY_RIGHT_MARGIN, + view, "show-right-margin", + G_SETTINGS_BIND_GET); + + g_settings_bind (editor_settings, GEDIT_SETTINGS_BACKGROUND_PATTERN, + view, "background-pattern", + G_SETTINGS_BIND_GET); + + g_settings_bind (editor_settings, GEDIT_SETTINGS_RIGHT_MARGIN_POSITION, + view, "right-margin-position", + G_SETTINGS_BIND_GET); + + g_settings_bind (editor_settings, GEDIT_SETTINGS_HIGHLIGHT_CURRENT_LINE, +@@ -693,31 +720,244 @@ gedit_view_class_init (GeditViewClass *klass) + GDK_CONTROL_MASK, + "change-case", 1, + G_TYPE_ENUM, GTK_SOURCE_CHANGE_CASE_LOWER); + + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_asciitilde, + GDK_CONTROL_MASK, + "change-case", 1, + G_TYPE_ENUM, GTK_SOURCE_CHANGE_CASE_TOGGLE); + } + + /** + * gedit_view_new: + * @doc: a #GeditDocument + * + * Creates a new #GeditView object displaying the @doc document. + * @doc cannot be %NULL. + * + * Returns: a new #GeditView. + */ + GtkWidget * + gedit_view_new (GeditDocument *doc) + { + g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), NULL); + + return g_object_new (GEDIT_TYPE_VIEW, + "buffer", doc, + NULL); + } + ++void ++gedit_view_cut_clipboard (GeditView *view) ++{ ++ GtkTextBuffer *buffer; ++ GtkClipboard *clipboard; ++ ++ gedit_debug (DEBUG_VIEW); ++ ++ g_return_if_fail (GEDIT_IS_VIEW (view)); ++ ++ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); ++ ++ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (view), ++ GDK_SELECTION_CLIPBOARD); ++ ++ gtk_text_buffer_cut_clipboard (buffer, ++ clipboard, ++ gtk_text_view_get_editable (GTK_TEXT_VIEW (view))); ++ ++ gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view), ++ gtk_text_buffer_get_insert (buffer), ++ GEDIT_VIEW_SCROLL_MARGIN, ++ FALSE, ++ 0.0, ++ 0.0); ++} ++ ++void ++gedit_view_copy_clipboard (GeditView *view) ++{ ++ GtkTextBuffer *buffer; ++ GtkClipboard *clipboard; ++ ++ gedit_debug (DEBUG_VIEW); ++ ++ g_return_if_fail (GEDIT_IS_VIEW (view)); ++ ++ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); ++ ++ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (view), ++ GDK_SELECTION_CLIPBOARD); ++ ++ gtk_text_buffer_copy_clipboard (buffer, clipboard); ++ ++ /* on copy do not scroll, we are already on screen */ ++} ++ ++void ++gedit_view_paste_clipboard (GeditView *view) ++{ ++ GtkTextBuffer *buffer; ++ GtkClipboard *clipboard; ++ ++ gedit_debug (DEBUG_VIEW); ++ ++ g_return_if_fail (GEDIT_IS_VIEW (view)); ++ ++ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); ++ ++ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (view), ++ GDK_SELECTION_CLIPBOARD); ++ ++ gtk_text_buffer_paste_clipboard (buffer, ++ clipboard, ++ NULL, ++ gtk_text_view_get_editable (GTK_TEXT_VIEW (view))); ++ ++ gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view), ++ gtk_text_buffer_get_insert (buffer), ++ GEDIT_VIEW_SCROLL_MARGIN, ++ FALSE, ++ 0.0, ++ 0.0); ++} ++ ++/** ++ * gedit_view_delete_selection: ++ * @view: a #GeditView ++ * ++ * Deletes the text currently selected in the #GtkTextBuffer associated ++ * to the view and scroll to the cursor position. ++ */ ++void ++gedit_view_delete_selection (GeditView *view) ++{ ++ GtkTextBuffer *buffer; ++ ++ gedit_debug (DEBUG_VIEW); ++ ++ g_return_if_fail (GEDIT_IS_VIEW (view)); ++ ++ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); ++ ++ gtk_text_buffer_delete_selection (buffer, ++ TRUE, ++ gtk_text_view_get_editable (GTK_TEXT_VIEW (view))); ++ ++ gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view), ++ gtk_text_buffer_get_insert (buffer), ++ GEDIT_VIEW_SCROLL_MARGIN, ++ FALSE, ++ 0.0, ++ 0.0); ++} ++ ++/** ++ * gedit_view_select_all: ++ * @view: a #GeditView ++ * ++ * Selects all the text. ++ */ ++void ++gedit_view_select_all (GeditView *view) ++{ ++ GtkTextBuffer *buffer; ++ GtkTextIter start; ++ GtkTextIter end; ++ ++ gedit_debug (DEBUG_VIEW); ++ ++ g_return_if_fail (GEDIT_IS_VIEW (view)); ++ ++ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); ++ ++ gtk_text_buffer_get_bounds (buffer, &start, &end); ++ gtk_text_buffer_select_range (buffer, &start, &end); ++} ++ ++/** ++ * gedit_view_scroll_to_cursor: ++ * @view: a #GeditView ++ * ++ * Scrolls the @view to the cursor position. ++ */ ++void ++gedit_view_scroll_to_cursor (GeditView *view) ++{ ++ GtkTextBuffer *buffer; ++ ++ gedit_debug (DEBUG_VIEW); ++ ++ g_return_if_fail (GEDIT_IS_VIEW (view)); ++ ++ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); ++ ++ gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view), ++ gtk_text_buffer_get_insert (buffer), ++ 0.25, ++ FALSE, ++ 0.0, ++ 0.0); ++} ++ ++static void ++update_css_provider (GeditView *view) ++{ ++ gchar *str; ++ gchar *css; ++ ++ g_assert (GEDIT_IS_VIEW (view)); ++ g_assert (view->priv->font_desc != NULL); ++ ++ str = gedit_pango_font_description_to_css (view->priv->font_desc); ++ css = g_strdup_printf ("textview { %s }", str ? str : ""); ++ gtk_css_provider_load_from_data (view->priv->css_provider, css, -1, NULL); ++ ++ g_free (css); ++ g_free (str); ++} ++ ++/** ++ * gedit_view_set_font: ++ * @view: a #GeditView ++ * @default_font: whether to reset to the default font ++ * @font_name: the name of the font to use ++ * ++ * If @default_font is #TRUE, resets the font of the @view to the default font. ++ * Otherwise sets it to @font_name. ++ */ ++void ++gedit_view_set_font (GeditView *view, ++ gboolean default_font, ++ const gchar *font_name) ++{ ++ gedit_debug (DEBUG_VIEW); ++ ++ g_return_if_fail (GEDIT_IS_VIEW (view)); ++ ++ g_clear_pointer (&view->priv->font_desc, pango_font_description_free); ++ ++ if (default_font) ++ { ++ GeditSettings *settings; ++ gchar *font; ++ ++ settings = _gedit_settings_get_singleton (); ++ font = gedit_settings_get_system_font (settings); ++ ++ view->priv->font_desc = pango_font_description_from_string (font); ++ g_free (font); ++ } ++ else ++ { ++ g_return_if_fail (font_name != NULL); ++ ++ view->priv->font_desc = pango_font_description_from_string (font_name); ++ } ++ ++ g_return_if_fail (view->priv->font_desc != NULL); ++ ++ update_css_provider (view); ++} ++ + /* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-view.h b/gedit/gedit-view.h +index c05d68553..7f2ae3ae2 100644 +--- a/gedit/gedit-view.h ++++ b/gedit/gedit-view.h +@@ -1,67 +1,85 @@ + /* + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi + * Copyright (C) 2002-2005 Paolo Maggi + * + * 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, see . + */ + + #ifndef GEDIT_VIEW_H + #define GEDIT_VIEW_H + ++#include ++ + #include +-#include ++#include + + G_BEGIN_DECLS + + #define GEDIT_TYPE_VIEW (gedit_view_get_type ()) + #define GEDIT_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_VIEW, GeditView)) + #define GEDIT_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GEDIT_TYPE_VIEW, GeditViewClass)) + #define GEDIT_IS_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GEDIT_TYPE_VIEW)) + #define GEDIT_IS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_VIEW)) + #define GEDIT_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_VIEW, GeditViewClass)) + + typedef struct _GeditView GeditView; + typedef struct _GeditViewClass GeditViewClass; + typedef struct _GeditViewPrivate GeditViewPrivate; + + struct _GeditView + { +- TeplView view; ++ GtkSourceView view; + + /*< private >*/ + GeditViewPrivate *priv; + }; + + struct _GeditViewClass + { +- TeplViewClass parent_class; ++ GtkSourceViewClass parent_class; + + void (*drop_uris) (GeditView *view, + gchar **uri_list); + + gpointer padding; + }; + + GType gedit_view_get_type (void); + + GtkWidget * gedit_view_new (GeditDocument *doc); + ++void gedit_view_cut_clipboard (GeditView *view); ++ ++void gedit_view_copy_clipboard (GeditView *view); ++ ++void gedit_view_paste_clipboard (GeditView *view); ++ ++void gedit_view_delete_selection (GeditView *view); ++ ++void gedit_view_select_all (GeditView *view); ++ ++void gedit_view_scroll_to_cursor (GeditView *view); ++ ++void gedit_view_set_font (GeditView *view, ++ gboolean default_font, ++ const gchar *font_name); ++ + G_END_DECLS + + #endif /* GEDIT_VIEW_H */ + + /* ex:set ts=8 noet: */ +diff --git a/gedit/gedit-window-private.h b/gedit/gedit-window-private.h +index 60f8ba706..380f6bdc5 100644 +--- a/gedit/gedit-window-private.h ++++ b/gedit/gedit-window-private.h +@@ -1,112 +1,119 @@ + /* + * gedit-window-private.h + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi + * + * 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 + * MERCHANWINDOWILITY 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, see . + */ + + #ifndef GEDIT_WINDOW_PRIVATE_H + #define GEDIT_WINDOW_PRIVATE_H + + #include + + #include "gedit/gedit-window.h" + #include "gedit-message-bus.h" + #include "gedit-settings.h" + #include "gedit-multi-notebook.h" ++#include "gedit-open-document-selector.h" + + G_BEGIN_DECLS + + /* WindowPrivate is in a separate .h so that we can access it from gedit-commands */ + + struct _GeditWindowPrivate + { + GSettings *editor_settings; + GSettings *ui_settings; + GSettings *window_settings; + + GeditMultiNotebook *multi_notebook; + + GtkWidget *side_panel; + GtkWidget *side_stack_switcher; + GtkWidget *side_panel_inline_stack_switcher; + GtkWidget *bottom_panel_box; + GtkWidget *bottom_panel; + + GtkWidget *hpaned; + GtkWidget *vpaned; + + GeditMessageBus *message_bus; + PeasExtensionSet *extensions; + + /* Widgets for fullscreen mode */ ++ GtkWidget *fullscreen_controls; + GtkWidget *fullscreen_eventbox; +- GtkRevealer *fullscreen_revealer; + GtkWidget *fullscreen_headerbar; +- GtkWidget *fullscreen_new_button; + GtkMenuButton *fullscreen_gear_button; +- GtkMenuButton *fullscreen_open_recent_button; ++ ++ GtkWidget *fullscreen_new_button; ++ GtkWidget *fullscreen_open_button; ++ GtkWidget *fullscreen_open_document_popover; ++ GeditOpenDocumentSelector *fullscreen_open_document_selector; + + /* statusbar and context ids for statusbar messages */ + GtkWidget *statusbar; + GtkWidget *line_col_button; + GtkWidget *tab_width_button; + GtkWidget *language_button; + GtkWidget *language_button_label; + GtkWidget *language_popover; + guint generic_message_cid; + guint tip_message_cid; + guint bracket_match_message_cid; + guint tab_width_id; + guint language_changed_id; + guint wrap_mode_changed_id; + + /* Headerbars */ + GtkWidget *titlebar_paned; + GtkWidget *side_headerbar; + GtkWidget *headerbar; + +- GtkWidget *new_button; ++ GtkWidget *open_document_popover; ++ GtkWidget *new_button; ++ GtkWidget *open_button; ++ GeditOpenDocumentSelector *open_document_selector; + + GtkMenuButton *gear_button; + + gint num_tabs_with_error; + + gint width; + gint height; + GdkWindowState window_state; + + gint side_panel_size; + gint bottom_panel_size; + + GeditWindowState state; + + guint inhibition_cookie; + + gint bottom_panel_item_removed_handler_id; + + GtkWindowGroup *window_group; + + gchar *file_chooser_folder_uri; + + gchar *direct_save_uri; + + GSList *closed_docs_stack; + + guint removing_tabs : 1; + guint dispose_has_run : 1; + + guint in_fullscreen_eventbox : 1; +diff --git a/gedit/gedit-window.c b/gedit/gedit-window.c +index 2f9a2076f..b08a1dbcc 100644 +--- a/gedit/gedit-window.c ++++ b/gedit/gedit-window.c +@@ -1,86 +1,92 @@ + /* + * gedit-window.c + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi + * + * 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, see . + */ + + #include "config.h" + + #include "gedit-window.h" + + #include + #include + #include + + #include + #include +-#include + + #include "gedit-window-private.h" + #include "gedit-app.h" + #include "gedit-app-private.h" ++#include "gedit-recent.h" + #include "gedit-notebook.h" + #include "gedit-notebook-popup-menu.h" + #include "gedit-multi-notebook.h" + #include "gedit-statusbar.h" + #include "gedit-tab.h" + #include "gedit-tab-private.h" + #include "gedit-view-frame.h" ++#include "gedit-view-centering.h" + #include "gedit-utils.h" + #include "gedit-commands.h" + #include "gedit-commands-private.h" + #include "gedit-debug.h" + #include "gedit-document.h" + #include "gedit-document-private.h" + #include "gedit-documents-panel.h" + #include "gedit-plugins-engine.h" + #include "gedit-window-activatable.h" + #include "gedit-enum-types.h" + #include "gedit-dirs.h" + #include "gedit-status-menu-button.h" + #include "gedit-settings.h" + #include "gedit-menu-stack-switcher.h" ++#include "gedit-highlight-mode-selector.h" ++#include "gedit-open-document-selector.h" ++ ++#define TAB_WIDTH_DATA "GeditWindowTabWidthData" ++#define FULLSCREEN_ANIMATION_SPEED 500 + + enum + { + PROP_0, + PROP_STATE, + LAST_PROP + }; + + static GParamSpec *properties[LAST_PROP]; + + enum + { + TAB_ADDED, + TAB_REMOVED, + TABS_REORDERED, + ACTIVE_TAB_CHANGED, + ACTIVE_TAB_STATE_CHANGED, + LAST_SIGNAL + }; + + static guint signals[LAST_SIGNAL]; + + enum + { + TARGET_URI_LIST = 100, + TARGET_XDNDDIRECTSAVE + }; + + static const GtkTargetEntry drop_types [] = { + { "XdndDirectSave0", 0, TARGET_XDNDDIRECTSAVE }, /* XDS Protocol Type */ +@@ -131,63 +137,63 @@ save_panels_state (GeditWindow *window) + { + g_settings_set_string (window->priv->window_settings, + GEDIT_SETTINGS_SIDE_PANEL_ACTIVE_PAGE, + panel_page); + } + + if (window->priv->bottom_panel_size > 0) + { + g_settings_set_int (window->priv->window_settings, + GEDIT_SETTINGS_BOTTOM_PANEL_SIZE, + window->priv->bottom_panel_size); + } + + panel_page = gtk_stack_get_visible_child_name (GTK_STACK (window->priv->bottom_panel)); + if (panel_page != NULL) + { + g_settings_set_string (window->priv->window_settings, + GEDIT_SETTINGS_BOTTOM_PANEL_ACTIVE_PAGE, + panel_page); + } + + g_settings_apply (window->priv->window_settings); + } + + static void + save_window_state (GtkWidget *widget) + { + GeditWindow *window = GEDIT_WINDOW (widget); + + if ((window->priv->window_state & +- (GDK_WINDOW_STATE_MAXIMIZED | GDK_WINDOW_STATE_FULLSCREEN)) == 0) ++ (GDK_WINDOW_STATE_MAXIMIZED | GDK_WINDOW_STATE_FULLSCREEN)) == 0) + { +- gtk_window_get_size (GTK_WINDOW (widget), &window->priv->width, &window->priv->height); ++ gtk_window_get_size (GTK_WINDOW (widget), &window->priv->width, &window->priv->height); + + g_settings_set (window->priv->window_settings, GEDIT_SETTINGS_WINDOW_SIZE, + "(ii)", window->priv->width, window->priv->height); + } + } + + static void + gedit_window_dispose (GObject *object) + { + GeditWindow *window; + + gedit_debug (DEBUG_WINDOW); + + window = GEDIT_WINDOW (object); + + /* Stop tracking removal of panels otherwise we always + * end up with thinking we had no panel active, since they + * should all be removed below */ + if (window->priv->bottom_panel_item_removed_handler_id != 0) + { + g_signal_handler_disconnect (window->priv->bottom_panel, + window->priv->bottom_panel_item_removed_handler_id); + window->priv->bottom_panel_item_removed_handler_id = 0; + } + + /* First of all, force collection so that plugins + * really drop some of the references. + */ + peas_engine_garbage_collect (PEAS_ENGINE (gedit_plugins_engine_get_default ())); + +@@ -204,142 +210,160 @@ gedit_window_dispose (GObject *object) + + peas_engine_garbage_collect (PEAS_ENGINE (gedit_plugins_engine_get_default ())); + + window->priv->dispose_has_run = TRUE; + } + + g_clear_object (&window->priv->message_bus); + g_clear_object (&window->priv->window_group); + + /* We must free the settings after saving the panels */ + g_clear_object (&window->priv->editor_settings); + g_clear_object (&window->priv->ui_settings); + g_clear_object (&window->priv->window_settings); + + /* Now that there have broken some reference loops, + * force collection again. + */ + peas_engine_garbage_collect (PEAS_ENGINE (gedit_plugins_engine_get_default ())); + + g_clear_object (&window->priv->side_stack_switcher); + + /* GTK+/GIO unref the action map in an idle. For the last GeditWindow, + * the application quits before the idle, so the action map is not + * unreffed, and some objects are not finalized on application shutdown + * (GeditView for example). + * So this is just for making the debugging of object references a bit + * nicer. + */ + remove_actions (window); + +- window->priv->fullscreen_open_recent_button = NULL; +- + G_OBJECT_CLASS (gedit_window_parent_class)->dispose (object); + } + + static void + gedit_window_finalize (GObject *object) + { + GeditWindow *window = GEDIT_WINDOW (object); + + g_free (window->priv->file_chooser_folder_uri); + g_slist_free_full (window->priv->closed_docs_stack, (GDestroyNotify)g_object_unref); + + G_OBJECT_CLASS (gedit_window_parent_class)->finalize (object); + } + ++/* Center the view when the window is in fullscreen mode. */ ++static void ++update_view_centering (GeditTab *tab, ++ gpointer user_data) ++{ ++ GeditViewFrame *view_frame; ++ GeditViewCentering *view_centering; ++ gboolean is_fullscreen; ++ ++ view_frame = _gedit_tab_get_view_frame (tab); ++ view_centering = gedit_view_frame_get_view_centering (view_frame); ++ ++ is_fullscreen = GPOINTER_TO_BOOLEAN (user_data); ++ gedit_view_centering_set_centered (view_centering, is_fullscreen); ++} ++ + static void + update_fullscreen (GeditWindow *window, + gboolean is_fullscreen) + { + GAction *fullscreen_action; + + _gedit_multi_notebook_set_show_tabs (window->priv->multi_notebook, !is_fullscreen); + + if (is_fullscreen) + { + gtk_widget_hide (window->priv->statusbar); + } + else + { + if (g_settings_get_boolean (window->priv->ui_settings, "statusbar-visible")) + { + gtk_widget_show (window->priv->statusbar); + } + } + ++ gedit_multi_notebook_foreach_tab (window->priv->multi_notebook, ++ (GtkCallback)update_view_centering, ++ GBOOLEAN_TO_POINTER (is_fullscreen)); ++ + #ifndef OS_OSX + if (is_fullscreen) + { + gtk_widget_show_all (window->priv->fullscreen_eventbox); + } + else + { + gtk_widget_hide (window->priv->fullscreen_eventbox); + } + #endif + + fullscreen_action = g_action_map_lookup_action (G_ACTION_MAP (window), + "fullscreen"); + + g_simple_action_set_state (G_SIMPLE_ACTION (fullscreen_action), + g_variant_new_boolean (is_fullscreen)); + } + + static gboolean + gedit_window_window_state_event (GtkWidget *widget, + GdkEventWindowState *event) + { + GeditWindow *window = GEDIT_WINDOW (widget); + + window->priv->window_state = event->new_window_state; + + g_settings_set_int (window->priv->window_settings, GEDIT_SETTINGS_WINDOW_STATE, + window->priv->window_state); + + if ((event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) != 0) + { + update_fullscreen (window, (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) != 0); + } + + return GTK_WIDGET_CLASS (gedit_window_parent_class)->window_state_event (widget, event); + } + + static gboolean + gedit_window_configure_event (GtkWidget *widget, + GdkEventConfigure *event) + { + GeditWindow *window = GEDIT_WINDOW (widget); + + if (gtk_widget_get_realized (widget) && + (window->priv->window_state & +- (GDK_WINDOW_STATE_MAXIMIZED | GDK_WINDOW_STATE_FULLSCREEN)) == 0) ++ (GDK_WINDOW_STATE_MAXIMIZED | GDK_WINDOW_STATE_FULLSCREEN)) == 0) + { + save_window_state (widget); + } + + return GTK_WIDGET_CLASS (gedit_window_parent_class)->configure_event (widget, event); + } + + /* + * GtkWindow catches keybindings for the menu items _before_ passing them to + * the focused widget. This is unfortunate and means that pressing ctrl+V + * in an entry on a panel ends up pasting text in the TextView. + * Here we override GtkWindow's handler to do the same things that it + * does, but in the opposite order and then we chain up to the grand + * parent handler, skipping gtk_window_key_press_event. + */ + static gboolean + gedit_window_key_press_event (GtkWidget *widget, + GdkEventKey *event) + { + static gpointer grand_parent_class = NULL; + + GtkWindow *window = GTK_WINDOW (widget); + gboolean handled = FALSE; + + if (grand_parent_class == NULL) + { + grand_parent_class = g_type_class_peek_parent (gedit_window_parent_class); + } + + /* handle focus widget key events */ +@@ -427,76 +451,78 @@ gedit_window_class_init (GeditWindowClass *klass) + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GeditWindowClass, tabs_reordered), + NULL, NULL, NULL, + G_TYPE_NONE, + 0); + signals[ACTIVE_TAB_CHANGED] = + g_signal_new ("active-tab-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GeditWindowClass, active_tab_changed), + NULL, NULL, NULL, + G_TYPE_NONE, + 1, + GEDIT_TYPE_TAB); + signals[ACTIVE_TAB_STATE_CHANGED] = + g_signal_new ("active-tab-state-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GeditWindowClass, active_tab_state_changed), + NULL, NULL, NULL, + G_TYPE_NONE, + 0); + + /* Bind class to template */ + gtk_widget_class_set_template_from_resource (widget_class, + "/org/gnome/gedit/ui/gedit-window.ui"); + gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, titlebar_paned); + gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, side_headerbar); + gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, headerbar); + gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, new_button); ++ gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, open_button); + gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, gear_button); + gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, hpaned); + gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, side_panel); + gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, side_panel_inline_stack_switcher); + gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, vpaned); + gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, multi_notebook); + gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, bottom_panel_box); + gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, bottom_panel); + gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, statusbar); + gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, language_button); + gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, tab_width_button); + gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, line_col_button); ++ gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, fullscreen_controls); + gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, fullscreen_eventbox); +- gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, fullscreen_revealer); + gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, fullscreen_headerbar); + gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, fullscreen_new_button); ++ gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, fullscreen_open_button); + gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, fullscreen_gear_button); + } + + static void + received_clipboard_contents (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + GeditWindow *window) + { + GeditTab *tab; + gboolean enabled; + GAction *action; + + /* getting clipboard contents is async, so we need to + * get the current tab and its state */ + + tab = gedit_window_get_active_tab (window); + + if (tab != NULL) + { + GeditTabState state; + gboolean state_normal; + + state = gedit_tab_get_state (tab); + state_normal = (state == GEDIT_TAB_STATE_NORMAL); + + enabled = state_normal && + gtk_selection_data_targets_include_text (selection_data); + } + else + { +@@ -738,138 +764,178 @@ update_actions_sensitivity (GeditWindow *window) + + /* We disable File->Quit/SaveAll/CloseAll while printing to avoid to have two + operations (save and print/print preview) that uses the message area at + the same time (may be we can remove this limitation in the future) */ + /* We disable File->Quit/CloseAll if state is saving since saving cannot be + cancelled (may be we can remove this limitation in the future) */ + action = g_action_map_lookup_action (G_ACTION_MAP (g_application_get_default ()), + "quit"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), + !(window->priv->state & GEDIT_WINDOW_STATE_SAVING) && + !(window->priv->state & GEDIT_WINDOW_STATE_PRINTING)); + + action = g_action_map_lookup_action (G_ACTION_MAP (window), "save-all"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), + !(window->priv->state & GEDIT_WINDOW_STATE_PRINTING) && + num_tabs > 0); + + action = g_action_map_lookup_action (G_ACTION_MAP (window), "close-all"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), + num_tabs > 0 && + !(window->priv->state & GEDIT_WINDOW_STATE_SAVING) && + !(window->priv->state & GEDIT_WINDOW_STATE_PRINTING) && + num_tabs > 0); + + peas_extension_set_foreach (window->priv->extensions, + (PeasExtensionSetForeachFunc) extension_update_state, + window); + } + + static void +-language_chooser_show_cb (TeplLanguageChooser *language_chooser, +- GeditWindow *window) ++on_recent_chooser_item_activated (GeditOpenDocumentSelector *open_document_selector, ++ gchar *uri, ++ GeditWindow *window) ++{ ++ GFile *location; ++ GeditView *active_view; ++ ++ g_return_if_fail (GEDIT_WINDOW (window)); ++ g_return_if_fail (GEDIT_OPEN_DOCUMENT_SELECTOR (open_document_selector)); ++ ++ /* TODO: get_current_file when exists */ ++ location = g_file_new_for_uri (uri); ++ ++ if (location) ++ { ++ GSList *locations = NULL; ++ GSList *loaded = NULL; ++ ++ locations = g_slist_prepend (locations, (gpointer) location); ++ loaded = gedit_commands_load_locations (window, locations, NULL, 0, 0); ++ ++ /* if it doesn't contain just 1 element */ ++ if (!loaded || loaded->next) ++ { ++ gedit_recent_remove_if_local (location); ++ } ++ ++ g_slist_free (locations); ++ g_slist_free (loaded); ++ ++ g_object_unref (location); ++ } ++ ++ /* Needed to close the popover when activating the same ++ * document as the current one */ ++ active_view = gedit_window_get_active_view (window); ++ gtk_widget_grab_focus (GTK_WIDGET (active_view)); ++} ++ ++static void ++language_selector_show_cb (GeditHighlightModeSelector *selector, ++ GeditWindow *window) + { + GeditDocument *active_document; + + active_document = gedit_window_get_active_document (window); + if (active_document != NULL) + { + GtkSourceLanguage *language; + + language = gedit_document_get_language (active_document); +- tepl_language_chooser_select_language (language_chooser, language); ++ gedit_highlight_mode_selector_select_language (selector, language); + } + } + + static void +-language_activated_cb (TeplLanguageChooser *language_chooser, +- GtkSourceLanguage *language, +- GeditWindow *window) ++language_selected_cb (GeditHighlightModeSelector *selector, ++ GtkSourceLanguage *language, ++ GeditWindow *window) + { + GeditDocument *active_document; + + active_document = gedit_window_get_active_document (window); + if (active_document != NULL) + { + gedit_document_set_language (active_document, language); + } + +- gtk_widget_hide (window->priv->language_popover); ++ gtk_widget_hide (GTK_WIDGET (window->priv->language_popover)); + } + + static void + setup_statusbar (GeditWindow *window) + { +- TeplLanguageChooserWidget *language_chooser; ++ GeditHighlightModeSelector *selector; + + gedit_debug (DEBUG_WINDOW); + + window->priv->generic_message_cid = gtk_statusbar_get_context_id + (GTK_STATUSBAR (window->priv->statusbar), "generic_message"); + window->priv->tip_message_cid = gtk_statusbar_get_context_id + (GTK_STATUSBAR (window->priv->statusbar), "tip_message"); + window->priv->bracket_match_message_cid = gtk_statusbar_get_context_id + (GTK_STATUSBAR (window->priv->statusbar), "bracket_match_message"); + + g_settings_bind (window->priv->ui_settings, + "statusbar-visible", + window->priv->statusbar, + "visible", + G_SETTINGS_BIND_GET); + + /* Line Col button */ + gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (window->priv->line_col_button), + _gedit_app_get_line_col_menu (GEDIT_APP (g_application_get_default ()))); + + /* Tab Width button */ + gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (window->priv->tab_width_button), + _gedit_app_get_tab_width_menu (GEDIT_APP (g_application_get_default ()))); + + /* Language button */ + window->priv->language_popover = gtk_popover_new (window->priv->language_button); + gtk_menu_button_set_popover (GTK_MENU_BUTTON (window->priv->language_button), + window->priv->language_popover); + +- language_chooser = tepl_language_chooser_widget_new (); ++ selector = gedit_highlight_mode_selector_new (); + +- g_signal_connect (language_chooser, ++ g_signal_connect (selector, + "show", +- G_CALLBACK (language_chooser_show_cb), ++ G_CALLBACK (language_selector_show_cb), + window); + +- g_signal_connect (language_chooser, +- "language-activated", +- G_CALLBACK (language_activated_cb), ++ g_signal_connect (selector, ++ "language-selected", ++ G_CALLBACK (language_selected_cb), + window); + +- gtk_container_add (GTK_CONTAINER (window->priv->language_popover), GTK_WIDGET (language_chooser)); +- gtk_widget_show (GTK_WIDGET (language_chooser)); ++ gtk_container_add (GTK_CONTAINER (window->priv->language_popover), GTK_WIDGET (selector)); ++ gtk_widget_show (GTK_WIDGET (selector)); + } + + static GeditWindow * + clone_window (GeditWindow *origin) + { + GeditWindow *window; + GdkScreen *screen; + GeditApp *app; + const gchar *panel_page; + + gedit_debug (DEBUG_WINDOW); + + app = GEDIT_APP (g_application_get_default ()); + + screen = gtk_window_get_screen (GTK_WINDOW (origin)); + window = gedit_app_create_window (app, screen); + + gtk_window_set_default_size (GTK_WINDOW (window), + origin->priv->width, + origin->priv->height); + + if ((origin->priv->window_state & GDK_WINDOW_STATE_MAXIMIZED) != 0) + gtk_window_maximize (GTK_WINDOW (window)); + else + gtk_window_unmaximize (GTK_WINDOW (window)); + + if ((origin->priv->window_state & GDK_WINDOW_STATE_STICKY) != 0) + gtk_window_stick (GTK_WINDOW (window)); + else + gtk_window_unstick (GTK_WINDOW (window)); +@@ -1017,81 +1083,81 @@ set_title (GeditWindow *window) + gedit_app_set_window_title (GEDIT_APP (g_application_get_default ()), + window, + "gedit"); + gtk_header_bar_set_title (GTK_HEADER_BAR (window->priv->headerbar), + "gedit"); + gtk_header_bar_set_subtitle (GTK_HEADER_BAR (window->priv->headerbar), + NULL); + gtk_header_bar_set_title (GTK_HEADER_BAR (window->priv->fullscreen_headerbar), + "gedit"); + gtk_header_bar_set_subtitle (GTK_HEADER_BAR (window->priv->fullscreen_headerbar), + NULL); + return; + } + + doc = gedit_tab_get_document (tab); + g_return_if_fail (doc != NULL); + + file = gedit_document_get_file (doc); + + name = gedit_document_get_short_name_for_display (doc); + + len = g_utf8_strlen (name, -1); + + /* if the name is awfully long, truncate it and be done with it, + * otherwise also show the directory (ellipsized if needed) + */ + if (len > MAX_TITLE_LENGTH) + { + gchar *tmp; + +- tmp = tepl_utils_str_middle_truncate (name, +- MAX_TITLE_LENGTH); ++ tmp = gedit_utils_str_middle_truncate (name, ++ MAX_TITLE_LENGTH); + g_free (name); + name = tmp; + } + else + { + GFile *location = gtk_source_file_get_location (file); + + if (location != NULL) + { + gchar *str = gedit_utils_location_get_dirname_for_display (location); + + /* use the remaining space for the dir, but use a min of 20 chars + * so that we do not end up with a dirname like "(a...b)". + * This means that in the worst case when the filename is long 99 + * we have a title long 99 + 20, but I think it's a rare enough + * case to be acceptable. It's justa darn title afterall :) + */ +- dirname = tepl_utils_str_middle_truncate (str, +- MAX (20, MAX_TITLE_LENGTH - len)); ++ dirname = gedit_utils_str_middle_truncate (str, ++ MAX (20, MAX_TITLE_LENGTH - len)); + g_free (str); + } + } + + if (gtk_text_buffer_get_modified (GTK_TEXT_BUFFER (doc))) + { + gchar *tmp_name; + + tmp_name = g_strdup_printf ("*%s", name); + g_free (name); + + name = tmp_name; + } + + if (gtk_source_file_is_readonly (file)) + { + title = g_strdup_printf ("%s [%s]", + name, _("Read-Only")); + + if (dirname != NULL) + { + main_title = g_strdup_printf ("%s [%s] (%s) - gedit", + name, + _("Read-Only"), + dirname); + subtitle = dirname; + } + else + { + main_title = g_strdup_printf ("%s [%s] - gedit", +@@ -1663,142 +1729,138 @@ drag_drop_cb (GtkWidget *widget, + + found = gtk_target_list_find (target_list, target, &info); + g_assert (found); + + if (info == TARGET_XDNDDIRECTSAVE) + { + gchar *uri; + uri = gedit_utils_set_direct_save_filename (context); + + if (uri != NULL) + { + g_free (window->priv->direct_save_uri); + window->priv->direct_save_uri = uri; + } + } + + gtk_drag_get_data (GTK_WIDGET (widget), context, + target, time); + } + } + + /* Handle drops on the GeditView */ + static void + drop_uris_cb (GtkWidget *widget, + gchar **uri_list, + GeditWindow *window) + { + load_uris_from_drop (window, uri_list); + } + +-static void +-update_fullscreen_revealer_state (GeditWindow *window) ++static gboolean ++on_fullscreen_controls_enter_notify_event (GtkWidget *widget, ++ GdkEventCrossing *event, ++ GeditWindow *window) + { +- gboolean open_recent_menu_is_active; +- gboolean hamburger_menu_is_active; ++ window->priv->in_fullscreen_eventbox = TRUE; + +- open_recent_menu_is_active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (window->priv->fullscreen_open_recent_button)); +- hamburger_menu_is_active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (window->priv->fullscreen_gear_button)); ++ gtk_revealer_set_reveal_child (GTK_REVEALER (window->priv->fullscreen_controls), TRUE); + +- gtk_revealer_set_reveal_child (window->priv->fullscreen_revealer, +- (window->priv->in_fullscreen_eventbox || +- open_recent_menu_is_active || +- hamburger_menu_is_active)); ++ return FALSE; + } + + static gboolean +-on_fullscreen_eventbox_enter_notify_event (GtkWidget *fullscreen_eventbox, +- GdkEventCrossing *event, +- GeditWindow *window) ++real_fullscreen_controls_leave_notify_event (gpointer data) + { +- window->priv->in_fullscreen_eventbox = TRUE; +- update_fullscreen_revealer_state (window); ++ GeditWindow *window = GEDIT_WINDOW (data); ++ gboolean hamburger_menu_state; ++ gboolean fullscreen_open_button_state; + +- return GDK_EVENT_PROPAGATE; +-} ++ hamburger_menu_state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (window->priv->fullscreen_gear_button)); ++ fullscreen_open_button_state = ++ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (window->priv->fullscreen_open_button)); + +-static gboolean +-on_fullscreen_eventbox_leave_notify_event (GtkWidget *fullscreen_eventbox, +- GdkEventCrossing *event, +- GeditWindow *window) +-{ +- if (-1.0 <= event->y && event->y <= 0.0) +- { +- /* Ignore the event. +- * +- * Leave notify events are received with -1 <= y <= 0 +- * coordinates, although the GeditWindow is in fullscreen mode +- * and when there are no screens above (it's maybe a bug in an +- * underlying library). +- * If we hide the headerbar when those events happen, then it +- * makes the headerbar to be shown/hidden a lot of time in a +- * short period of time, i.e. a "stuttering". In other words +- * lots of leave/enter events are received when moving the mouse +- * upwards on the screen when the mouse is already at the top. +- * The expected leave event has a positive event->y value being +- * >= to the height of the headerbar (approximately +- * 40 <= y <= 50). So clearly when we receive a leave event with +- * event->y <= 0, it means that the mouse has left the eventbox +- * on the wrong side. +- * The -1.0 <= event->y is there (instead of just <= 0.0) in the +- * case that there is another screen *above*, even if this +- * heuristic/workaround is not perfect in that case. But that +- * case is quite rare, so it's probably a good enough solution. +- * +- * Note that apparently the "stuttering" occurs only on an Xorg +- * session, not on Wayland (tested with GNOME). +- * +- * If you see a better solution... +- */ +- return GDK_EVENT_PROPAGATE; ++ window->priv->in_fullscreen_eventbox = FALSE; ++ ++ if (!hamburger_menu_state && !fullscreen_open_button_state) ++ { ++ gtk_revealer_set_reveal_child (GTK_REVEALER (window->priv->fullscreen_controls), FALSE); + } + +- window->priv->in_fullscreen_eventbox = FALSE; +- update_fullscreen_revealer_state (window); ++ return G_SOURCE_REMOVE; ++} ++ ++/* this idle is needed because the toggled signal from gear button is received ++ * after the leave event from the event box ( which is automatically triggered when user ++ * bring up the gear menu */ ++static gboolean ++on_fullscreen_controls_leave_notify_event (GtkWidget *widget, ++ GdkEventCrossing *event, ++ GeditWindow *window) ++{ ++ g_idle_add (real_fullscreen_controls_leave_notify_event, window); + + return GDK_EVENT_PROPAGATE; + } + + static void +-setup_fullscreen_eventbox (GeditWindow *window) ++fullscreen_controls_setup (GeditWindow *window) + { +- gtk_widget_set_size_request (window->priv->fullscreen_eventbox, -1, 1); +- gtk_widget_hide (window->priv->fullscreen_eventbox); ++ GeditWindowPrivate *priv = window->priv; + +- g_signal_connect (window->priv->fullscreen_eventbox, ++ g_signal_connect (priv->fullscreen_eventbox, + "enter-notify-event", +- G_CALLBACK (on_fullscreen_eventbox_enter_notify_event), ++ G_CALLBACK (on_fullscreen_controls_enter_notify_event), + window); + +- g_signal_connect (window->priv->fullscreen_eventbox, ++ g_signal_connect (priv->fullscreen_eventbox, + "leave-notify-event", +- G_CALLBACK (on_fullscreen_eventbox_leave_notify_event), ++ G_CALLBACK (on_fullscreen_controls_leave_notify_event), ++ window); ++ ++ gtk_widget_set_size_request (GTK_WIDGET (window->priv->fullscreen_eventbox), -1, 1); ++ gtk_widget_hide (window->priv->fullscreen_eventbox); ++ ++ priv->fullscreen_open_document_popover = gtk_popover_new (priv->fullscreen_open_button); ++ gtk_menu_button_set_popover (GTK_MENU_BUTTON (priv->fullscreen_open_button), ++ priv->fullscreen_open_document_popover); ++ ++ window->priv->fullscreen_open_document_selector = gedit_open_document_selector_new (window); ++ ++ gtk_container_add (GTK_CONTAINER (priv->fullscreen_open_document_popover), ++ GTK_WIDGET (priv->fullscreen_open_document_selector)); ++ ++ gtk_widget_show_all (GTK_WIDGET (priv->fullscreen_open_document_selector)); ++ ++ g_signal_connect (window->priv->fullscreen_open_document_selector, ++ "file-activated", ++ G_CALLBACK (on_recent_chooser_item_activated), + window); + } + + static void + empty_search_notify_cb (GeditDocument *doc, + GParamSpec *pspec, + GeditWindow *window) + { + if (doc == gedit_window_get_active_document (window)) + { + update_actions_sensitivity (window); + } + } + + static void + can_undo (GeditDocument *doc, + GParamSpec *pspec, + GeditWindow *window) + { + if (doc == gedit_window_get_active_document (window)) + { + update_actions_sensitivity (window); + } + } + + static void + can_redo (GeditDocument *doc, + GParamSpec *pspec, + GeditWindow *window) + { +@@ -1858,61 +1920,61 @@ on_tab_added (GeditMultiNotebook *multi, + update_actions_sensitivity (window); + + view = gedit_tab_get_view (tab); + doc = gedit_tab_get_document (tab); + file = gedit_document_get_file (doc); + + /* IMPORTANT: remember to disconnect the signal in notebook_tab_removed + * if a new signal is connected here */ + + g_signal_connect (tab, + "notify::name", + G_CALLBACK (sync_name), + window); + g_signal_connect (tab, + "notify::state", + G_CALLBACK (sync_state), + window); + g_signal_connect (tab, + "notify::can-close", + G_CALLBACK (sync_can_close), + window); + g_signal_connect (tab, + "drop_uris", + G_CALLBACK (drop_uris_cb), + window); + g_signal_connect (doc, + "bracket-matched", + G_CALLBACK (bracket_matched_cb), + window); + g_signal_connect (doc, +- "tepl-cursor-moved", ++ "cursor-moved", + G_CALLBACK (update_cursor_position_statusbar), + window); + g_signal_connect (doc, + "notify::empty-search", + G_CALLBACK (empty_search_notify_cb), + window); + g_signal_connect (doc, + "notify::can-undo", + G_CALLBACK (can_undo), + window); + g_signal_connect (doc, + "notify::can-redo", + G_CALLBACK (can_redo), + window); + g_signal_connect (doc, + "notify::has-selection", + G_CALLBACK (selection_changed), + window); + g_signal_connect (view, + "notify::overwrite", + G_CALLBACK (overwrite_mode_changed), + window); + g_signal_connect (view, + "notify::editable", + G_CALLBACK (editable_changed), + window); + g_signal_connect (file, + "notify::read-only", + G_CALLBACK (readonly_changed), + window); +@@ -2117,64 +2179,77 @@ on_show_popup_menu (GeditMultiNotebook *multi, + } + + menu = gedit_notebook_popup_menu_new (window, tab); + + g_signal_connect (menu, + "selection-done", + G_CALLBACK (gtk_widget_destroy), + NULL); + + gtk_widget_show (menu); + gtk_menu_popup_at_pointer (GTK_MENU (menu), (GdkEvent *)event); + } + + static void + on_notebook_changed (GeditMultiNotebook *mnb, + GParamSpec *pspec, + GeditWindow *window) + { + update_actions_sensitivity (window); + } + + static void + on_notebook_removed (GeditMultiNotebook *mnb, + GeditNotebook *notebook, + GeditWindow *window) + { + update_actions_sensitivity (window); + } + + static void +-on_fullscreen_toggle_button_toggled (GtkToggleButton *fullscreen_toggle_button, +- GeditWindow *window) ++on_fullscreen_gear_button_toggled (GtkToggleButton *fullscreen_gear_button, ++ GeditWindow *window) + { +- update_fullscreen_revealer_state (window); ++ gboolean button_active = gtk_toggle_button_get_active (fullscreen_gear_button); ++ ++ gtk_revealer_set_reveal_child (GTK_REVEALER (window->priv->fullscreen_controls), ++ button_active || window->priv->in_fullscreen_eventbox); ++} ++ ++static void ++on_fullscreen_file_menu_button_toggled (GtkMenuButton *fullscreen_open_button, ++ GeditWindow *window) ++{ ++ gboolean button_active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fullscreen_open_button)); ++ ++ gtk_revealer_set_reveal_child (GTK_REVEALER (window->priv->fullscreen_controls), ++ button_active || window->priv->in_fullscreen_eventbox); + } + + static void + side_panel_size_allocate (GtkWidget *widget, + GtkAllocation *allocation, + GeditWindow *window) + { + window->priv->side_panel_size = allocation->width; + } + + static void + bottom_panel_size_allocate (GtkWidget *widget, + GtkAllocation *allocation, + GeditWindow *window) + { + window->priv->bottom_panel_size = allocation->height; + } + + static void + hpaned_restore_position (GtkWidget *widget, + GeditWindow *window) + { + gint pos; + + gedit_debug_message (DEBUG_WINDOW, + "Restoring hpaned position: side panel size %d", + window->priv->side_panel_size); + + pos = MAX (100, window->priv->side_panel_size); + gtk_paned_set_position (GTK_PANED (window->priv->hpaned), pos); +@@ -2510,60 +2585,72 @@ clipboard_owner_change (GtkClipboard *clipboard, + + static void + window_realized (GtkWidget *window, + gpointer *data) + { + GtkClipboard *clipboard; + + clipboard = gtk_widget_get_clipboard (window, + GDK_SELECTION_CLIPBOARD); + + g_signal_connect (clipboard, + "owner_change", + G_CALLBACK (clipboard_owner_change), + window); + } + + static void + window_unrealized (GtkWidget *window, + gpointer *data) + { + GtkClipboard *clipboard; + + clipboard = gtk_widget_get_clipboard (window, + GDK_SELECTION_CLIPBOARD); + + g_signal_handlers_disconnect_by_func (clipboard, + G_CALLBACK (clipboard_owner_change), + window); + } + ++static void ++check_window_is_active (GeditWindow *window, ++ GParamSpec *property, ++ gpointer useless) ++{ ++ if (window->priv->window_state & GDK_WINDOW_STATE_FULLSCREEN) ++ { ++ gtk_widget_set_visible (window->priv->fullscreen_eventbox, ++ gtk_window_is_active (GTK_WINDOW (window))); ++ } ++} ++ + static void + extension_added (PeasExtensionSet *extensions, + PeasPluginInfo *info, + PeasExtension *exten, + GeditWindow *window) + { + gedit_window_activatable_activate (GEDIT_WINDOW_ACTIVATABLE (exten)); + } + + static void + extension_removed (PeasExtensionSet *extensions, + PeasPluginInfo *info, + PeasExtension *exten, + GeditWindow *window) + { + gedit_window_activatable_deactivate (GEDIT_WINDOW_ACTIVATABLE (exten)); + } + + static GActionEntry win_entries[] = { + { "new-tab", _gedit_cmd_file_new }, + { "open", _gedit_cmd_file_open }, + { "revert", _gedit_cmd_file_revert }, + { "reopen-closed-tab", _gedit_cmd_file_reopen_closed_tab }, + { "save", _gedit_cmd_file_save }, + { "save-as", _gedit_cmd_file_save_as }, + { "save-all", _gedit_cmd_file_save_all }, + { "close", _gedit_cmd_file_close }, + { "close-all", _gedit_cmd_file_close_all }, + { "print", _gedit_cmd_file_print }, + { "focus-active-view", NULL, NULL, "false", _gedit_cmd_view_focus_active }, +@@ -2581,188 +2668,145 @@ static GActionEntry win_entries[] = { + { "previous-tab-group", _gedit_cmd_documents_previous_tab_group }, + { "next-tab-group", _gedit_cmd_documents_next_tab_group }, + { "previous-document", _gedit_cmd_documents_previous_document }, + { "next-document", _gedit_cmd_documents_next_document }, + { "move-to-new-window", _gedit_cmd_documents_move_to_new_window }, + { "undo", _gedit_cmd_edit_undo }, + { "redo", _gedit_cmd_edit_redo }, + { "cut", _gedit_cmd_edit_cut }, + { "copy", _gedit_cmd_edit_copy }, + { "paste", _gedit_cmd_edit_paste }, + { "delete", _gedit_cmd_edit_delete }, + { "select-all", _gedit_cmd_edit_select_all }, + { "highlight-mode", _gedit_cmd_view_highlight_mode }, + { "overwrite-mode", NULL, NULL, "false", _gedit_cmd_edit_overwrite_mode } + }; + + static void + sync_fullscreen_actions (GeditWindow *window, + gboolean fullscreen) + { + GtkMenuButton *button; + GPropertyAction *action; + + button = fullscreen ? window->priv->fullscreen_gear_button : window->priv->gear_button; + g_action_map_remove_action (G_ACTION_MAP (window), "hamburger-menu"); + action = g_property_action_new ("hamburger-menu", button, "active"); + g_action_map_add_action (G_ACTION_MAP (window), G_ACTION (action)); + g_object_unref (action); + } + +-static void +-init_amtk_application_window (GeditWindow *gedit_window) +-{ +- AmtkApplicationWindow *amtk_window; +- +- amtk_window = amtk_application_window_get_from_gtk_application_window (GTK_APPLICATION_WINDOW (gedit_window)); +- amtk_application_window_set_statusbar (amtk_window, GTK_STATUSBAR (gedit_window->priv->statusbar)); +-} +- +-static GtkWidget * +-create_open_buttons (GeditWindow *window, +- GtkMenuButton **open_recent_button) +-{ +- GtkWidget *hbox; +- GtkStyleContext *style_context; +- GtkWidget *open_dialog_button; +- GtkWidget *my_open_recent_button; +- AmtkApplicationWindow *amtk_window; +- GtkWidget *recent_menu; +- +- /* It currently needs to be a GtkBox, not a GtkGrid, because GtkGrid and +- * GTK_STYLE_CLASS_LINKED doesn't work as expected in a RTL locale. +- * Probably a GtkGrid bug. +- */ +- hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); +- style_context = gtk_widget_get_style_context (hbox); +- gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_LINKED); +- +- open_dialog_button = gtk_button_new_with_mnemonic (_("_Open")); +- gtk_widget_set_tooltip_text (open_dialog_button, _("Open a file")); +- gtk_actionable_set_action_name (GTK_ACTIONABLE (open_dialog_button), "win.open"); +- +- my_open_recent_button = gtk_menu_button_new (); +- gtk_widget_set_tooltip_text (my_open_recent_button, _("Open a recently used file")); +- +- amtk_window = amtk_application_window_get_from_gtk_application_window (GTK_APPLICATION_WINDOW (window)); +- recent_menu = amtk_application_window_create_open_recent_menu (amtk_window); +- gtk_menu_button_set_popup (GTK_MENU_BUTTON (my_open_recent_button), recent_menu); +- +- gtk_container_add (GTK_CONTAINER (hbox), open_dialog_button); +- gtk_container_add (GTK_CONTAINER (hbox), my_open_recent_button); +- gtk_widget_show_all (hbox); +- +- if (open_recent_button != NULL) +- { +- *open_recent_button = GTK_MENU_BUTTON (my_open_recent_button); +- } +- +- return hbox; +-} +- +-static void +-init_open_buttons (GeditWindow *window) +-{ +- gtk_container_add_with_properties (GTK_CONTAINER (window->priv->headerbar), +- create_open_buttons (window, NULL), +- "position", 0, /* The first on the left. */ +- NULL); +- +- gtk_container_add_with_properties (GTK_CONTAINER (window->priv->fullscreen_headerbar), +- create_open_buttons (window, &(window->priv->fullscreen_open_recent_button)), +- "position", 0, /* The first on the left. */ +- NULL); +- +- g_signal_connect (GTK_TOGGLE_BUTTON (window->priv->fullscreen_open_recent_button), +- "toggled", +- G_CALLBACK (on_fullscreen_toggle_button_toggled), +- window); +-} +- + static void + gedit_window_init (GeditWindow *window) + { + GtkTargetList *tl; + GMenuModel *hamburger_menu; + + gedit_debug (DEBUG_WINDOW); + + window->priv = gedit_window_get_instance_private (window); + + window->priv->removing_tabs = FALSE; + window->priv->state = GEDIT_WINDOW_STATE_NORMAL; + window->priv->inhibition_cookie = 0; + window->priv->dispose_has_run = FALSE; ++ window->priv->fullscreen_controls = NULL; + window->priv->direct_save_uri = NULL; + window->priv->closed_docs_stack = NULL; + window->priv->editor_settings = g_settings_new ("org.gnome.gedit.preferences.editor"); + window->priv->ui_settings = g_settings_new ("org.gnome.gedit.preferences.ui"); + + /* window settings are applied only once the window is closed. We do not + want to keep writing to disk when the window is dragged around */ + window->priv->window_settings = g_settings_new ("org.gnome.gedit.state.window"); + g_settings_delay (window->priv->window_settings); + + window->priv->message_bus = gedit_message_bus_new (); + + gtk_widget_init_template (GTK_WIDGET (window)); +- init_amtk_application_window (window); +- init_open_buttons (window); + + g_action_map_add_action_entries (G_ACTION_MAP (window), + win_entries, + G_N_ELEMENTS (win_entries), + window); + + window->priv->window_group = gtk_window_group_new (); + gtk_window_group_add_window (window->priv->window_group, GTK_WINDOW (window)); + +- setup_fullscreen_eventbox (window); ++ /* Setup file popover and file dialog */ ++ window->priv->open_document_popover = gtk_popover_new (window->priv->open_button); ++ gtk_menu_button_set_popover (GTK_MENU_BUTTON (window->priv->open_button), ++ window->priv->open_document_popover); ++ ++ window->priv->open_document_selector = gedit_open_document_selector_new (window); ++ ++ gtk_container_add (GTK_CONTAINER (window->priv->open_document_popover), ++ GTK_WIDGET (window->priv->open_document_selector)); ++ ++ gtk_widget_show_all (GTK_WIDGET (window->priv->open_document_selector)); ++ ++ g_signal_connect (window->priv->open_document_selector, ++ "file-activated", ++ G_CALLBACK (on_recent_chooser_item_activated), ++ window); ++ ++ fullscreen_controls_setup (window); + sync_fullscreen_actions (window, FALSE); + ++ g_object_bind_property (gedit_open_document_selector_get_search_entry (window->priv->open_document_selector), ++ "text", ++ gedit_open_document_selector_get_search_entry (window->priv->fullscreen_open_document_selector), ++ "text", ++ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); ++ + hamburger_menu = _gedit_app_get_hamburger_menu (GEDIT_APP (g_application_get_default ())); + if (hamburger_menu) + { + gtk_menu_button_set_menu_model (window->priv->gear_button, hamburger_menu); + gtk_menu_button_set_menu_model (window->priv->fullscreen_gear_button, hamburger_menu); + } + else + { + gtk_widget_hide (GTK_WIDGET (window->priv->gear_button)); + gtk_widget_hide (GTK_WIDGET (window->priv->fullscreen_gear_button)); + gtk_widget_set_no_show_all (GTK_WIDGET (window->priv->gear_button), TRUE); + gtk_widget_set_no_show_all (GTK_WIDGET (window->priv->fullscreen_gear_button), TRUE); + } + ++ g_signal_connect (GTK_TOGGLE_BUTTON (window->priv->fullscreen_open_button), ++ "toggled", ++ G_CALLBACK (on_fullscreen_file_menu_button_toggled), ++ window); ++ + g_signal_connect (GTK_TOGGLE_BUTTON (window->priv->fullscreen_gear_button), + "toggled", +- G_CALLBACK (on_fullscreen_toggle_button_toggled), ++ G_CALLBACK (on_fullscreen_gear_button_toggled), + window); + + /* Setup status bar */ + setup_statusbar (window); + + /* Setup main area */ + g_signal_connect (window->priv->multi_notebook, + "notebook-removed", + G_CALLBACK (on_notebook_removed), + window); + g_signal_connect (window->priv->multi_notebook, + "notify::active-notebook", + G_CALLBACK (on_notebook_changed), + window); + + g_signal_connect (window->priv->multi_notebook, + "tab-added", + G_CALLBACK (on_tab_added), + window); + + g_signal_connect (window->priv->multi_notebook, + "tab-removed", + G_CALLBACK (on_tab_removed), + window); + + g_signal_connect (window->priv->multi_notebook, + "switch-tab", + G_CALLBACK (tab_switched), + window); + +@@ -2822,60 +2866,66 @@ gedit_window_init (GeditWindow *window) + { + tl = gtk_target_list_new (drop_types, G_N_ELEMENTS (drop_types)); + gtk_drag_dest_set_target_list (GTK_WIDGET (window), tl); + gtk_target_list_unref (tl); + } + + gtk_target_list_add_uri_targets (tl, TARGET_URI_LIST); + + /* connect instead of override, so that we can + * share the cb code with the view */ + g_signal_connect (window, + "drag_data_received", + G_CALLBACK (drag_data_received_cb), + NULL); + g_signal_connect (window, + "drag_drop", + G_CALLBACK (drag_drop_cb), + NULL); + + /* we can get the clipboard only after the widget + * is realized */ + g_signal_connect (window, + "realize", + G_CALLBACK (window_realized), + NULL); + g_signal_connect (window, + "unrealize", + G_CALLBACK (window_unrealized), + NULL); + ++ /* Check if the window is active for fullscreen */ ++ g_signal_connect (window, ++ "notify::is-active", ++ G_CALLBACK (check_window_is_active), ++ NULL); ++ + gedit_debug_message (DEBUG_WINDOW, "Update plugins ui"); + + window->priv->extensions = peas_extension_set_new (PEAS_ENGINE (gedit_plugins_engine_get_default ()), + GEDIT_TYPE_WINDOW_ACTIVATABLE, + "window", window, + NULL); + g_signal_connect (window->priv->extensions, + "extension-added", + G_CALLBACK (extension_added), + window); + g_signal_connect (window->priv->extensions, + "extension-removed", + G_CALLBACK (extension_removed), + window); + peas_extension_set_foreach (window->priv->extensions, + (PeasExtensionSetForeachFunc) extension_added, + window); + + /* set visibility of panels. + * This needs to be done after plugins activatation */ + init_panels_visibility (window); + + update_actions_sensitivity (window); + + gedit_debug_message (DEBUG_WINDOW, "END"); + } + + /** + * gedit_window_get_active_view: + * @window: a #GeditWindow +diff --git a/gedit/gedit.c b/gedit/gedit.c +index fcffdaca6..ee3769537 100644 +--- a/gedit/gedit.c ++++ b/gedit/gedit.c +@@ -1,67 +1,66 @@ + /* + * gedit.c + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi + * + * 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, see . + */ + + #include "config.h" + + #include "gedit-app.h" + + #if defined OS_OSX + # include "gedit-app-osx.h" + #elif defined G_OS_WIN32 + # include "gedit-app-win32.h" + #endif + ++#include + #include + #include +-#include + + #include "gedit-dirs.h" + #include "gedit-debug.h" +-#include "gedit-factory.h" + #include "gedit-settings.h" + + #ifdef G_OS_WIN32 + #include + static GModule *libgedit_dll = NULL; + + /* This code must live in gedit.exe, not in libgedit.dll, since the whole + * point is to find and load libgedit.dll. + */ + static gboolean + gedit_w32_load_private_dll (void) + { + gchar *dllpath; + gchar *prefix; + + prefix = g_win32_get_package_installation_directory_of_module (NULL); + + if (prefix != NULL) + { + /* Instead of g_module_open () it may be possible to do any of the + * following: + * A) Change PATH to "${dllpath}/lib/gedit;$PATH" + * B) Call SetDllDirectory ("${dllpath}/lib/gedit") + * C) Call AddDllDirectory ("${dllpath}/lib/gedit") + * But since we only have one library, and its name is known, may as well + * use gmodule. + */ + dllpath = g_build_filename (prefix, "lib", "gedit", "lib" PACKAGE_STRING ".dll", NULL); + g_free (prefix); + +@@ -90,91 +89,86 @@ gedit_w32_load_private_dll (void) + + static void + gedit_w32_unload_private_dll (void) + { + if (libgedit_dll) + { + g_module_close (libgedit_dll); + libgedit_dll = NULL; + } + } + #endif /* G_OS_WIN32 */ + + static void + setup_i18n (void) + { + const gchar *dir; + + setlocale (LC_ALL, ""); + + dir = gedit_dirs_get_gedit_locale_dir (); + bindtextdomain (GETTEXT_PACKAGE, dir); + + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + } + + int + main (int argc, char *argv[]) + { + GType type; +- GeditFactory *factory; + GeditApp *app; + gint status; + + #if defined OS_OSX + type = GEDIT_TYPE_APP_OSX; + #elif defined G_OS_WIN32 + if (!gedit_w32_load_private_dll ()) + { + return 1; + } + + type = GEDIT_TYPE_APP_WIN32; + #else + type = GEDIT_TYPE_APP; + #endif + + /* NOTE: we should not make any calls to the gedit API before the + * private library is loaded. + */ + gedit_dirs_init (); + + setup_i18n (); +- tepl_init (); +- factory = gedit_factory_new (); +- tepl_abstract_factory_set_singleton (TEPL_ABSTRACT_FACTORY (factory)); + + app = g_object_new (type, + "application-id", "org.gnome.gedit", + "flags", G_APPLICATION_HANDLES_COMMAND_LINE | G_APPLICATION_HANDLES_OPEN, + NULL); + + status = g_application_run (G_APPLICATION (app), argc, argv); + + gedit_settings_unref_singleton (); + + /* Break reference cycles caused by the PeasExtensionSet + * for GeditAppActivatable which holds a ref on the GeditApp + */ + g_object_run_dispose (G_OBJECT (app)); + + g_object_add_weak_pointer (G_OBJECT (app), (gpointer *) &app); + g_object_unref (app); + + if (app != NULL) + { + gedit_debug_message (DEBUG_APP, "Leaking with %i refs", + G_OBJECT (app)->ref_count); + } + +- tepl_finalize (); + gedit_dirs_shutdown (); + + #ifdef G_OS_WIN32 + gedit_w32_unload_private_dll (); + #endif + + return status; + } + + /* ex:set ts=8 noet: */ +diff --git a/gedit/meson.build b/gedit/meson.build +index 214c2b9a5..51472f12d 100644 +--- a/gedit/meson.build ++++ b/gedit/meson.build +@@ -1,149 +1,166 @@ + libgedit_public_headers = [ + 'gedit-app-activatable.h', + 'gedit-app.h', + 'gedit-commands.h', + 'gedit-debug.h', + 'gedit-document.h', + 'gedit-encodings-combo-box.h', + 'gedit-menu-extension.h', + 'gedit-message-bus.h', + 'gedit-message.h', ++ 'gedit-progress-info-bar.h', + 'gedit-statusbar.h', + 'gedit-tab.h', + 'gedit-utils.h', + 'gedit-view-activatable.h', + 'gedit-view.h', + 'gedit-window-activatable.h', + 'gedit-window.h', + ] + + libgedit_public_sources = [ + 'gedit-app-activatable.c', + 'gedit-app.c', + 'gedit-commands-file.c', + 'gedit-debug.c', + 'gedit-document.c', + 'gedit-encodings-combo-box.c', + 'gedit-menu-extension.c', + 'gedit-message-bus.c', + 'gedit-message.c', ++ 'gedit-progress-info-bar.c', + 'gedit-statusbar.c', + 'gedit-tab.c', + 'gedit-utils.c', + 'gedit-view-activatable.c', + 'gedit-view.c', ++ 'gedit-view-centering.c', + 'gedit-window-activatable.c', + 'gedit-window.c', + ] + + libgedit_private_headers = [ + 'gedit-app-osx.h', + 'gedit-app-win32.h', + 'gedit-close-confirmation-dialog.h', + 'gedit-dirs.h', + 'gedit-document-private.h', + 'gedit-documents-panel.h', + 'gedit-encoding-items.h', + 'gedit-encodings-dialog.h', +- 'gedit-factory.h', + 'gedit-file-chooser-dialog-gtk.h', + 'gedit-file-chooser-dialog.h', + 'gedit-file-chooser.h', + 'gedit-file-chooser-open-dialog.h', + 'gedit-file-chooser-open.h', + 'gedit-file-chooser-open-native.h', ++ 'gedit-highlight-mode-dialog.h', ++ 'gedit-highlight-mode-selector.h', + 'gedit-history-entry.h', + 'gedit-io-error-info-bar.h', + 'gedit-menu-stack-switcher.h', ++ 'gedit-metadata-manager.h', + 'gedit-multi-notebook.h', + 'gedit-notebook.h', + 'gedit-notebook-popup-menu.h', + 'gedit-notebook-stack-switcher.h', ++ 'gedit-open-document-selector.h', ++ 'gedit-open-document-selector-helper.h', ++ 'gedit-open-document-selector-store.h', ++ 'gedit-pango.h', + 'gedit-plugins-engine.h', + 'gedit-preferences-dialog.h', + 'gedit-print-job.h', + 'gedit-print-preview.h', + 'gedit-recent.h', +- 'gedit-recent-osx.h', + 'gedit-replace-dialog.h', + 'gedit-settings.h', + 'gedit-status-menu-button.h', + 'gedit-tab-label.h', ++ 'gedit-tab-private.h', ++ 'gedit-view-centering.h', + 'gedit-view-frame.h', + 'gedit-window-private.h', + ] + + libgedit_private_sources = [ + 'gedit-close-confirmation-dialog.c', + 'gedit-commands-documents.c', + 'gedit-commands-edit.c', + 'gedit-commands-file-print.c', + 'gedit-commands-help.c', + 'gedit-commands-search.c', + 'gedit-commands-view.c', + 'gedit-dirs.c', + 'gedit-documents-panel.c', + 'gedit-encoding-items.c', + 'gedit-encodings-dialog.c', +- 'gedit-factory.c', + 'gedit-file-chooser.c', + 'gedit-file-chooser-dialog.c', + 'gedit-file-chooser-dialog-gtk.c', + 'gedit-file-chooser-open.c', + 'gedit-file-chooser-open-dialog.c', + 'gedit-file-chooser-open-native.c', ++ 'gedit-highlight-mode-dialog.c', ++ 'gedit-highlight-mode-selector.c', + 'gedit-history-entry.c', + 'gedit-io-error-info-bar.c', + 'gedit-menu-stack-switcher.c', ++ 'gedit-metadata-manager.c', + 'gedit-multi-notebook.c', + 'gedit-notebook.c', + 'gedit-notebook-popup-menu.c', + 'gedit-notebook-stack-switcher.c', ++ 'gedit-open-document-selector.c', ++ 'gedit-open-document-selector-helper.c', ++ 'gedit-open-document-selector-store.c', ++ 'gedit-pango.c', + 'gedit-plugins-engine.c', + 'gedit-preferences-dialog.c', + 'gedit-print-job.c', + 'gedit-print-preview.c', + 'gedit-recent.c', + 'gedit-replace-dialog.c', + 'gedit-settings.c', + 'gedit-status-menu-button.c', + 'gedit-tab-label.c', + 'gedit-view-frame.c', + ] + + libgedit_c_args = [] + libgedit_link_args = [] + + libgedit_deps = [ + deps_basic_list, + libgd_dep, ++ libxml_dep, + ] + + if host_machine.system() == 'darwin' + libgedit_private_sources += [ + 'gedit-app-osx.m', + 'gedit-recent-osx.c', + ] + libgedit_c_args += [ + '-DOS_OSX=1', + ] + libgedit_link_args += [ + '-Wl,-framework', '-Wl,Foundation', + '-Wl,-framework', '-Wl,AppKit', + ] + libgedit_deps += [ + dependency('gtk-mac-integration-gtk3'), + ] + elif host_machine.system() == 'windows' + libgedit_private_sources += [ + 'gedit-app-win32.c', + ] + endif + + headers_install_dir = get_option('includedir') / 'gedit-@0@/gedit/'.format(api_version) + install_headers( + libgedit_public_headers, + install_dir: headers_install_dir, + ) + + libgedit_public_enum_types = gnome.mkenums_simple( +diff --git a/gedit/resources/css/gedit-style.css b/gedit/resources/css/gedit-style.css +index eb43a8233..7036567c4 100644 +--- a/gedit/resources/css/gedit-style.css ++++ b/gedit/resources/css/gedit-style.css +@@ -1,31 +1,43 @@ + .gedit-side-panel-paned.pane-separator:dir(ltr), + .gedit-side-panel-paned.pane-separator:hover:dir(ltr) { + border-radius: 0; + border-width: 0 1px 0 0; + } + + .gedit-side-panel-paned.pane-separator:dir(rtl), + .gedit-side-panel-paned.pane-separator:hover:dir(rtl) { + border-radius: 0; + border-width: 0 0 0 1px; + } + + .gedit-menu-stack-switcher { + padding: 12px; + } + ++.gedit-map-frame:dir(ltr) border { ++ border-width: 0 0 0 1px; ++} ++ ++.gedit-map-frame:dir(rtl) border { ++ border-width: 0 1px 0 0; ++} ++ ++.open-document-selector-treeview { ++ padding: 3px 6px 3px 6px; ++} ++ + statusbar frame { + border: none; + padding-left: 6px; + padding-right: 6px; + } + + statusbar button.flat { + border-radius: 0; + border-bottom: none; + } + + GeditFileBrowserWidget .small-button { + padding: 2px 4px; + } + +diff --git a/gedit/resources/css/gedit.adwaita.css b/gedit/resources/css/gedit.adwaita.css +index 784e72aa2..8377c62a9 100644 +--- a/gedit/resources/css/gedit.adwaita.css ++++ b/gedit/resources/css/gedit.adwaita.css +@@ -1,30 +1,55 @@ ++.open-document-selector-treeview:hover { ++ background-color: alpha(@theme_fg_color, 0.05); ++} ++ ++.open-document-selector-treeview:selected:hover { ++ background-color: @theme_selected_bg_color; ++} ++ ++/* Only normal state is handle */ ++.open-document-selector-name-label { ++ color: @theme_fg_color; ++} ++ ++/* Only normal state is handle */ ++.open-document-selector-path-label { ++ color: @theme_unfocused_fg_color; ++ font-size: smaller; ++} ++ ++/* Only normal state is handle */ ++.open-document-selector-match { ++ color: shade (@theme_fg_color, 0.6); ++ background-color: alpha(@warning_color, 0.4); ++} ++ + .gedit-document-panel { + background-color: @sidebar_bg; + } + + .gedit-document-panel:backdrop { + color: #b0b2b2; + } + + .gedit-document-panel row:selected:backdrop { + background-color: #8b8e8f; + } + + .gedit-document-panel-group-row, + .gedit-document-panel-group-row:hover { + border-top: 1px solid alpha(currentColor, 0.3); + } + + .gedit-document-panel-group-row:first-child, + .gedit-document-panel-group-row:first-child:hover { + border-top: 0px; + } + + /* Try to look as the notebook tab close button */ + .gedit-document-panel row button.flat { + padding: 0; + margin-top: 8px; + margin-bottom: 8px; + min-width: 18px; + min-height: 18px; + color: alpha(currentColor,0.3); +diff --git a/gedit/resources/gedit.gresource.xml.in b/gedit/resources/gedit.gresource.xml.in +index a5905110e..b0a70c185 100644 +--- a/gedit/resources/gedit.gresource.xml.in ++++ b/gedit/resources/gedit.gresource.xml.in +@@ -1,23 +1,27 @@ + + + + gtk/menus.ui + gtk/menus-common.ui + ui/gedit-encodings-dialog.ui + ui/gedit-preferences-dialog.ui + ui/gedit-replace-dialog.ui + ui/gedit-print-preview.ui + ui/gedit-print-preferences.ui ++ ui/gedit-progress-info-bar.ui + ui/gedit-status-menu-button.ui + ui/gedit-tab-label.ui + ui/gedit-view-frame.ui ++ ui/gedit-highlight-mode-dialog.ui ++ ui/gedit-highlight-mode-selector.ui + ui/gedit-window.ui ++ ui/gedit-open-document-selector.ui + ui/gedit-shortcuts.ui + ui/gedit-statusbar.ui + css/gedit-style.css + css/gedit.adwaita.css + css/gedit.highcontrast.css + + @OS_DEPENDENT_RESOURCE_FILES@ + + +diff --git a/gedit/resources/ui/gedit-highlight-mode-dialog.ui b/gedit/resources/ui/gedit-highlight-mode-dialog.ui +new file mode 100644 +index 000000000..31b8690bd +--- /dev/null ++++ b/gedit/resources/ui/gedit-highlight-mode-dialog.ui +@@ -0,0 +1,87 @@ ++ ++ ++ ++ ++ +diff --git a/gedit/resources/ui/gedit-highlight-mode-selector.ui b/gedit/resources/ui/gedit-highlight-mode-selector.ui +new file mode 100644 +index 000000000..1aa5c950e +--- /dev/null ++++ b/gedit/resources/ui/gedit-highlight-mode-selector.ui +@@ -0,0 +1,83 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ liststore ++ ++ ++ +diff --git a/gedit/resources/ui/gedit-open-document-selector.ui b/gedit/resources/ui/gedit-open-document-selector.ui +new file mode 100644 +index 000000000..9393ac46c +--- /dev/null ++++ b/gedit/resources/ui/gedit-open-document-selector.ui +@@ -0,0 +1,115 @@ ++ ++ ++ ++ ++ +diff --git a/gedit/resources/ui/gedit-preferences-dialog.ui b/gedit/resources/ui/gedit-preferences-dialog.ui +index f1eeed45c..1ce0755da 100644 +--- a/gedit/resources/ui/gedit-preferences-dialog.ui ++++ b/gedit/resources/ui/gedit-preferences-dialog.ui +@@ -78,72 +78,87 @@ + 1 + True + True + + + 0 + 0 + + + + + 1 + 1 + + + + + Display _statusbar + True + True + False + True + True + + + 0 + 2 + 2 + + ++ ++ ++ Display _overview map ++ True ++ True ++ False ++ True ++ True ++ ++ ++ 0 ++ 3 ++ 2 ++ ++ + + + Display _grid pattern + True + True + False + True + True + + + 0 +- 3 ++ 4 + 2 + + + + + 0 + 0 + + + + + True + False + 6 + + + True + False + 0 + Text Wrapping + + + + + + 0 + 0 + + + +@@ -603,62 +618,73 @@ + + + + True + False + True + True + 6 + + + True + False + 0 + Color Scheme + + + + + + 0 + 0 + + + + + True + False + True + True + +- ++ + True ++ True ++ True ++ True ++ etched-in ++ 200 ++ ++ ++ True ++ True ++ ++ + + + 0 + 0 + + + + + True + False + icons + 1 + + + True + False + True + Install scheme + Install scheme + Install Scheme + True + list-add-symbolic + + + False + True + + + + +diff --git a/gedit/resources/ui/gedit-progress-info-bar.ui b/gedit/resources/ui/gedit-progress-info-bar.ui +new file mode 100644 +index 000000000..3f733e4b6 +--- /dev/null ++++ b/gedit/resources/ui/gedit-progress-info-bar.ui +@@ -0,0 +1,88 @@ ++ ++ ++ ++ ++ +diff --git a/gedit/resources/ui/gedit-view-frame.ui b/gedit/resources/ui/gedit-view-frame.ui +index 4c783c711..779802db3 100644 +--- a/gedit/resources/ui/gedit-view-frame.ui ++++ b/gedit/resources/ui/gedit-view-frame.ui +@@ -1,47 +1,68 @@ + + + +