diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9090e51f05..bd13093a0c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: - name: Install Dependencies run: | apt update - apt install -y exuberant-ctags libeditorconfig-dev libgail-3-dev libgee-0.8-dev libgit2-glib-1.0-dev libgranite-dev libgtk-3-dev libgtksourceview-4-dev libgtkspell3-3-dev libhandy-1-dev libpeas-dev libsoup2.4-dev libvala-dev libvte-2.91-dev meson valac polkitd libpolkit-gobject-1-dev + apt install -y exuberant-ctags libeditorconfig-dev libgail-3-dev libgee-0.8-dev libgit2-glib-1.0-dev libgranite-dev libgtk-3-dev libgtksourceview-4-dev libgtkspell3-3-dev libhandy-1-dev libsoup2.4-dev libvala-dev libvte-2.91-dev meson valac polkitd libpolkit-gobject-1-dev - name: Build env: DESTDIR: out diff --git a/README.md b/README.md index 9f440e8b3e..e218248392 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,6 @@ You'll need the following dependencies: * libgtkspell3-3-dev * libgranite-dev >= 6.0.0 * libhandy-1-dev >= 0.90.0 -* libpeas-dev * libsoup2.4-dev * libvala-0.48-dev (or higher) * libvte-2.91-dev diff --git a/io.elementary.code.yml b/io.elementary.code.yml index c0764fbf43..75c1e8599c 100644 --- a/io.elementary.code.yml +++ b/io.elementary.code.yml @@ -33,17 +33,6 @@ modules: url: https://gitlab.gnome.org/GNOME/gtksourceview.git tag: '4.8.4' - - name: peas - buildsystem: meson - config-opts: - - '-Dgtk_doc=false' - - '-Ddemos=false' - - '-Dvapi=true' - sources: - - type: git - url: https://gitlab.gnome.org/GNOME/libpeas.git - tag: libpeas-1.34.0 - - name: git2-glib buildsystem: meson builddir: true diff --git a/meson.build b/meson.build index b600a80667..51bbf227c8 100644 --- a/meson.build +++ b/meson.build @@ -27,14 +27,13 @@ gnome = import('gnome') i18n = import('i18n') glib_dep = dependency('glib-2.0', version: '>=2.30.0') +gmodule_dep = dependency('gmodule-2.0', version: '>=2.30.0') gio_unix_dep = dependency('gio-unix-2.0', version: '>=2.20') gee_dep = dependency('gee-0.8', version: '>=0.8.5') gtk_dep = dependency('gtk+-3.0', version: '>=3.6.0') granite_dep = dependency('granite', version: '>=6.0.0') handy_dep = dependency('libhandy-1', version: '>=0.90.0') gtksourceview_dep = dependency('gtksourceview-4') -peas_dep = dependency('libpeas-1.0') -peasgtk_dep = dependency('libpeas-gtk-1.0') git_dep = dependency('libgit2-glib-1.0') fontconfig_dep = dependency('fontconfig') pangofc_dep = dependency('pangoft2') @@ -52,14 +51,13 @@ vala_dep = dependency('libvala-@0@'.format(vala_version)) dependencies = [ glib_dep, + gmodule_dep, gio_unix_dep, gee_dep, gtk_dep, granite_dep, handy_dep, gtksourceview_dep, - peas_dep, - peasgtk_dep, git_dep, fontconfig_dep, pangofc_dep, diff --git a/plugins/brackets-completion/brackets-completion.vala b/plugins/brackets-completion/brackets-completion.vala index bf3f4cf15c..9ae06f9627 100644 --- a/plugins/brackets-completion/brackets-completion.vala +++ b/plugins/brackets-completion/brackets-completion.vala @@ -2,7 +2,8 @@ /*** BEGIN LICENSE - Copyright (C) 2013 Mario Guerriero + Copyright (C) 2019-24 elementary, Inc. + 2013 Mario Guerriero This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3, as published by the Free Software Foundation. @@ -18,7 +19,7 @@ END LICENSE ***/ -public class Scratch.Plugins.BracketsCompletion : Peas.ExtensionBase, Peas.Activatable { +public class Scratch.Plugins.BracketsCompletion : Scratch.Plugins.PluginBase { Gee.HashMap brackets; Gee.HashMap keys; const string[] VALID_NEXT_CHARS = { @@ -30,12 +31,12 @@ public class Scratch.Plugins.BracketsCompletion : Peas.ExtensionBase, Peas.Activ private string previous_selection = ""; - Scratch.Services.Interface plugins; - public Object object { owned get; construct; } - - public void update_state () {} + public BracketsCompletion (PluginInfo info, Interface iface) { + base (info, iface); + } - public void activate () { + ulong doc_hook_handler = 0; + protected override void activate_internal () { brackets = new Gee.HashMap (); brackets["("] = ")"; brackets["["] = "]"; @@ -55,12 +56,11 @@ public class Scratch.Plugins.BracketsCompletion : Peas.ExtensionBase, Peas.Activ keys[Gdk.Key.quotedbl] = "\""; keys[Gdk.Key.grave] = "`"; - plugins = (Scratch.Services.Interface) object; - plugins.hook_document.connect (on_hook_document); + doc_hook_handler = iface.hook_document.connect (on_hook_document); } - public void deactivate () { - plugins.hook_document.disconnect (on_hook_document); + protected override void deactivate_internal () { + iface.disconnect (doc_hook_handler); } void on_hook_document (Scratch.Services.Document doc) { @@ -275,9 +275,9 @@ public class Scratch.Plugins.BracketsCompletion : Peas.ExtensionBase, Peas.Activ } } -[ModuleInit] -public void peas_register_types (GLib.TypeModule module) { - var objmodule = module as Peas.ObjectModule; - objmodule.register_extension_type (typeof (Peas.Activatable), - typeof (Scratch.Plugins.BracketsCompletion)); +public Scratch.Plugins.PluginBase module_init ( + Scratch.Plugins.PluginInfo info, + Scratch.Plugins.Interface iface +) { + return new Scratch.Plugins.BracketsCompletion (info, iface); } diff --git a/plugins/detect-indent/detect-indent.vala b/plugins/detect-indent/detect-indent.vala index 09fe9c1d24..b7e37454c5 100644 --- a/plugins/detect-indent/detect-indent.vala +++ b/plugins/detect-indent/detect-indent.vala @@ -1,16 +1,33 @@ -public class Scratch.Plugins.DetectIndent: Peas.ExtensionBase, Peas.Activatable { +// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- +/*** + BEGIN LICENSE + + Copyright (C) 2019-24 elementary, Inc. + 2013 LemonBoy + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License version 3, as published + by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranties of + MERCHANTABILITY, SATISFACTORY QUALITY, 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 + + END LICENSE +***/ +public class Scratch.Plugins.DetectIndent: Scratch.Plugins.PluginBase { const int MAX_LINES = 500; - Scratch.Services.Interface plugins; - public Object object {owned get; construct;} - - public void update_state () { + public DetectIndent (PluginInfo info, Interface iface) { + base (info, iface); } - public void activate () { - plugins = (Scratch.Services.Interface) object; - - plugins.hook_document.connect ((d) => { + ulong doc_hook_handler = 0; + protected override void activate_internal () { + doc_hook_handler = iface.hook_document.connect ((d) => { var view = d.source_view; if (!view.get_editable ()) { @@ -69,17 +86,14 @@ public class Scratch.Plugins.DetectIndent: Peas.ExtensionBase, Peas.Activatable }); } - public void deactivate () { - + protected override void deactivate_internal () { + iface.disconnect (doc_hook_handler); } - } -[ModuleInit] -public void peas_register_types (GLib.TypeModule module) { - var objmodule = module as Peas.ObjectModule; - objmodule.register_extension_type ( - typeof (Peas.Activatable), - typeof (Scratch.Plugins.DetectIndent) - ); +public Scratch.Plugins.PluginBase module_init ( + Scratch.Plugins.PluginInfo info, + Scratch.Plugins.Interface iface +) { + return new Scratch.Plugins.DetectIndent (info, iface); } diff --git a/plugins/editorconfig/editorconfig.vala b/plugins/editorconfig/editorconfig.vala index 8a5b8f4285..b1aa7fdf7f 100644 --- a/plugins/editorconfig/editorconfig.vala +++ b/plugins/editorconfig/editorconfig.vala @@ -1,37 +1,33 @@ -/* -* Copyright (c) 2018 elementary LLC. (https://github.com/elementary) -* -* 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 3 of the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -* Boston, MA 02110-1301 USA -*/ +// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- +/*** + BEGIN LICENSE -public class Scratch.Plugins.EditorConfigPlugin: Peas.ExtensionBase, Peas.Activatable { - Scratch.Services.Interface plugins; - public Object object { owned get; construct; } - private Code.FormatBar format_bar; + Copyright (C) 2018-24 elementary, Inc. + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License version 3, as published + by the Free Software Foundation. - public void update_state () { } + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranties of + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + PURPOSE. See the GNU General Public License for more details. - public void activate () { - plugins = (Scratch.Services.Interface) object; + You should have received a copy of the GNU General Public License along + with this program. If not, see - plugins.hook_toolbar.connect ((tb) => { - format_bar = tb.format_bar; - }); + END LICENSE +***/ - plugins.hook_document.connect ((d) => { +public class Scratch.Plugins.EditorConfigPlugin: Scratch.Plugins.PluginBase { + private Code.FormatBar format_bar; + + public EditorConfigPlugin (PluginInfo info, Interface iface) { + base (info, iface); + } + + ulong doc_hook_handler = 0; + protected override void activate_internal () { + doc_hook_handler = iface.hook_document.connect ((d) => { // Ensure use global settings by default format_bar.tab_style_set_by_editor_config = false; format_bar.tab_width_set_by_editor_config = false; @@ -83,13 +79,20 @@ public class Scratch.Plugins.EditorConfigPlugin: Peas.ExtensionBase, Peas.Activa } } }); + + iface.hook_toolbar.connect ((tb) => { + format_bar = tb.format_bar; + }); } - public void deactivate () { } + protected override void deactivate_internal () { + iface.disconnect (doc_hook_handler); + } } -[ModuleInit] -public void peas_register_types (GLib.TypeModule module) { - var objmodule = module as Peas.ObjectModule; - objmodule.register_extension_type (typeof (Peas.Activatable), typeof (Scratch.Plugins.EditorConfigPlugin)); +public Scratch.Plugins.PluginBase module_init ( + Scratch.Plugins.PluginInfo info, + Scratch.Plugins.Interface iface +) { + return new Scratch.Plugins.EditorConfigPlugin (info, iface); } diff --git a/plugins/fuzzy-search/fuzzy-search.vala b/plugins/fuzzy-search/fuzzy-search.vala index 2cff1cf2b7..d0f341f35d 100644 --- a/plugins/fuzzy-search/fuzzy-search.vala +++ b/plugins/fuzzy-search/fuzzy-search.vala @@ -1,19 +1,17 @@ /* * SPDX-License-Identifier: GPL-3.0-or-later - * SPDX-FileCopyrightText: 2023 elementary, Inc. + * SPDX-FileCopyrightText: 2023-24 elementary, Inc. * * Authored by: Marvin Ahlgrimm */ -public class Scratch.Plugins.FuzzySearch: Peas.ExtensionBase, Peas.Activatable { - public Object object { owned get; construct; } +public class Scratch.Plugins.FuzzySearch: Scratch.Plugins.PluginBase { private const uint ACCEL_KEY = Gdk.Key.F; private const Gdk.ModifierType ACCEL_MODTYPE = Gdk.ModifierType.MOD1_MASK; private Scratch.Services.FuzzySearchIndexer indexer; private MainWindow window = null; - private Scratch.Services.Interface plugins; private GLib.MenuItem fuzzy_menuitem; private GLib.Cancellable cancellable; @@ -33,14 +31,14 @@ public class Scratch.Plugins.FuzzySearch: Peas.ExtensionBase, Peas.Activatable { action_accelerators.set (ACTION_SHOW, @"$(Gdk.keyval_name (ACCEL_KEY))"); } - public void update_state () { - + public FuzzySearch (PluginInfo info, Interface iface) { + base (info, iface); } - public void activate () { - plugins = (Scratch.Services.Interface) object; - - plugins.hook_window.connect ((w) => { + ulong window_hook_handler = 0; + ulong folder_hook_handler = 0; + protected override void activate_internal () { + window_hook_handler = iface.hook_window.connect ((w) => { if (window != null) { return; } @@ -59,7 +57,7 @@ public class Scratch.Plugins.FuzzySearch: Peas.ExtensionBase, Peas.Activatable { folder_settings.changed["opened-folders"].connect (handle_opened_projects_change); }); - plugins.hook_folder_item_change.connect ((src, dest, event) => { + folder_hook_handler = iface.hook_folder_item_change.connect ((src, dest, event) => { if (indexer == null) { return; } @@ -68,6 +66,17 @@ public class Scratch.Plugins.FuzzySearch: Peas.ExtensionBase, Peas.Activatable { }); } + protected override void deactivate_internal () { + folder_settings.changed["opened-folders"].disconnect (handle_opened_projects_change); + remove_actions (); + if (cancellable != null) { + cancellable.cancel (); + } + + iface.disconnect (window_hook_handler); + iface.disconnect (folder_hook_handler); + } + private void add_actions () { if (actions == null) { actions = new SimpleActionGroup (); @@ -138,15 +147,6 @@ public class Scratch.Plugins.FuzzySearch: Peas.ExtensionBase, Peas.Activatable { popover.popup (); } - public void deactivate () { - folder_settings.changed["opened-folders"].disconnect (handle_opened_projects_change); - remove_actions (); - if (cancellable != null) { - cancellable.cancel (); - } - } - - private void handle_opened_projects_change () { var show_action = Utils.action_from_group (ACTION_SHOW, actions); string[] opened_folders = folder_settings.get_strv ("opened-folders"); @@ -154,11 +154,9 @@ public class Scratch.Plugins.FuzzySearch: Peas.ExtensionBase, Peas.Activatable { } } -[ModuleInit] -public void peas_register_types (GLib.TypeModule module) { - var objmodule = module as Peas.ObjectModule; - objmodule.register_extension_type ( - typeof (Peas.Activatable), - typeof (Scratch.Plugins.FuzzySearch) - ); +public Scratch.Plugins.PluginBase module_init ( + Scratch.Plugins.PluginInfo info, + Scratch.Plugins.Interface iface +) { + return new Scratch.Plugins.FuzzySearch (info, iface); } diff --git a/plugins/highlight-word-selection/highlight-word-selection.vala b/plugins/highlight-word-selection/highlight-word-selection.vala index 5896b5d43e..8f851daee1 100644 --- a/plugins/highlight-word-selection/highlight-word-selection.vala +++ b/plugins/highlight-word-selection/highlight-word-selection.vala @@ -1,8 +1,9 @@ // -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- /*** BEGIN LICENSE + Copyright (C) 2019-24 elementary, Inc. + 2013 Madelynn May - Copyright (C) 2013 Madelynn May This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3, as published by the Free Software Foundation. @@ -18,7 +19,7 @@ END LICENSE ***/ -public class Scratch.Plugins.HighlightSelectedWords : Peas.ExtensionBase, Peas.Activatable { +public class Scratch.Plugins.HighlightSelectedWords : Scratch.Plugins.PluginBase { Scratch.Widgets.SourceView current_source; Scratch.MainWindow? main_window = null; Gtk.SourceSearchContext? current_search_context = null; @@ -27,14 +28,14 @@ public class Scratch.Plugins.HighlightSelectedWords : Peas.ExtensionBase, Peas.A // Pneumonoultramicroscopicsilicovolcanoconiosis longest word in a major dictionary @ 45 private const uint SELECTION_HIGHLIGHT_MAX_CHARS = 45; - Scratch.Services.Interface plugins; - public Object object { owned get; construct; } - - public void update_state () {} + public HighlightSelectedWords (PluginInfo info, Interface iface) { + base (info, iface); + } - public void activate () { - plugins = (Scratch.Services.Interface) object; - plugins.hook_document.connect ((doc) => { + ulong window_hook_handler = 0; + ulong doc_hook_handler = 0; + protected override void activate_internal () { + doc_hook_handler = iface.hook_document.connect ((doc) => { if (current_source != null) { current_source.deselected.disconnect (on_deselection); current_source.selection_changed.disconnect (on_selection_changed); @@ -45,18 +46,28 @@ public class Scratch.Plugins.HighlightSelectedWords : Peas.ExtensionBase, Peas.A current_source.selection_changed.connect (on_selection_changed); }); - plugins.hook_window.connect ((w) => { + window_hook_handler = iface.hook_window.connect ((w) => { main_window = w; }); } + protected override void deactivate_internal () { + if (current_source != null) { + current_source.deselected.disconnect (on_deselection); + current_source.selection_changed.disconnect (on_selection_changed); + } + + iface.disconnect (window_hook_handler); + iface.disconnect (doc_hook_handler); + } + public void on_selection_changed (ref Gtk.TextIter start, ref Gtk.TextIter end) { var window_search_context = main_window != null ? main_window.search_bar.search_context : null; if (window_search_context == null || window_search_context.settings.search_text == "" || window_search_context.get_occurrences_count () == 0) { - // Perform plugin selection when there is no ongoing and successful search + // Perform plugin selection when there is no ongoing and successful search current_search_context = new Gtk.SourceSearchContext ( (Gtk.SourceBuffer)current_source.buffer, null @@ -139,18 +150,11 @@ public class Scratch.Plugins.HighlightSelectedWords : Peas.ExtensionBase, Peas.A current_search_context = null; } } - - public void deactivate () { - if (current_source != null) { - current_source.deselected.disconnect (on_deselection); - current_source.selection_changed.disconnect (on_selection_changed); - } - } } -[ModuleInit] -public void peas_register_types (TypeModule module) { - var objmodule = module as Peas.ObjectModule; - objmodule.register_extension_type (typeof (Peas.Activatable), - typeof (Scratch.Plugins.HighlightSelectedWords)); +public Scratch.Plugins.PluginBase module_init ( + Scratch.Plugins.PluginInfo info, + Scratch.Plugins.Interface iface +) { + return new Scratch.Plugins.HighlightSelectedWords (info, iface); } diff --git a/plugins/markdown-actions/markdown-actions.vala b/plugins/markdown-actions/markdown-actions.vala index 024e97c3b6..6a105967af 100644 --- a/plugins/markdown-actions/markdown-actions.vala +++ b/plugins/markdown-actions/markdown-actions.vala @@ -1,8 +1,9 @@ // -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- /*** BEGIN LICENSE + Copyright (C) 2024 elementary, Inc. + 2020 Igor Montagner - Copyright (C) 2020 Igor Montagner This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3, as published by the Free Software Foundation. @@ -18,17 +19,16 @@ END LICENSE ***/ -public class Code.Plugins.MarkdownActions : Peas.ExtensionBase, Peas.Activatable { +public class Scratch.Plugins.MarkdownActions : Scratch.Plugins.PluginBase { Scratch.Widgets.SourceView current_source; - Scratch.Services.Interface plugins; - public Object object { owned get; construct; } - - public void update_state () {} + public MarkdownActions (PluginInfo info, Interface iface) { + base (info, iface); + } - public void activate () { - plugins = (Scratch.Services.Interface) object; - plugins.hook_document.connect ((doc) => { + ulong doc_hook_handler = 0; + protected override void activate_internal () { + doc_hook_handler = iface.hook_document.connect ((doc) => { if (current_source != null) { current_source.key_press_event.disconnect (shortcut_handler); current_source.notify["language"].disconnect (configure_shortcuts); @@ -41,6 +41,15 @@ public class Code.Plugins.MarkdownActions : Peas.ExtensionBase, Peas.Activatable }); } + protected override void deactivate_internal () { + if (current_source != null) { + current_source.key_press_event.disconnect (shortcut_handler); + current_source.notify["language"].disconnect (configure_shortcuts); + } + + iface.disconnect (doc_hook_handler); + } + private void configure_shortcuts () { var lang = current_source.language; if (lang != null && lang.id == "markdown") { @@ -231,18 +240,11 @@ public class Code.Plugins.MarkdownActions : Peas.ExtensionBase, Peas.Activatable current_buffer.end_user_action (); go_back_n_chars (tag.length); } - - public void deactivate () { - if (current_source != null) { - current_source.key_press_event.disconnect (shortcut_handler); - current_source.notify["language"].disconnect (configure_shortcuts); - } - } } -[ModuleInit] -public void peas_register_types (TypeModule module) { - var objmodule = module as Peas.ObjectModule; - objmodule.register_extension_type (typeof (Peas.Activatable), - typeof (Code.Plugins.MarkdownActions)); +public Scratch.Plugins.PluginBase module_init ( + Scratch.Plugins.PluginInfo info, + Scratch.Plugins.Interface iface +) { + return new Scratch.Plugins.MarkdownActions (info, iface); } diff --git a/plugins/pastebin/pastebin.plugin b/plugins/pastebin/pastebin.plugin index 07a536c118..8bb8691cbe 100644 --- a/plugins/pastebin/pastebin.plugin +++ b/plugins/pastebin/pastebin.plugin @@ -1,5 +1,5 @@ [Plugin] -Module=libpastebin +Module=pastebin Loader=C IAge=2 Name=Pastebin diff --git a/plugins/pastebin/pastebin.vala b/plugins/pastebin/pastebin.vala index 3a4471dc21..a64762a374 100644 --- a/plugins/pastebin/pastebin.vala +++ b/plugins/pastebin/pastebin.vala @@ -1,8 +1,9 @@ // -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- /*** BEGIN LICENSE + Copyright (C) 2024 elementary, Inc. + 2011-2012 Giulio Collura - Copyright (C) 2011-2012 Giulio Collura This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3, as published by the Free Software Foundation. @@ -18,63 +19,13 @@ END LICENSE ***/ -namespace Scratch.Services { - public class PasteBin : GLib.Object { - public const string NEVER = "N"; - public const string TEN_MINUTES = "10M"; - public const string HOUR = "1H"; - public const string DAY = "1D"; - public const string MONTH = "1M"; - public const string PRIVATE = "1"; - public const string PUBLIC = "0"; - - public static bool submit (out string link, string paste_code, string paste_name, - string paste_private, string paste_expire_date, - string paste_format) { - - if (paste_code.length == 0) { link = "No text to paste"; return false; } - - string api_url = "https://pastebin.com/api/api_post.php"; - - var session = new Soup.Session (); - var message = new Soup.Message ("POST", api_url); - - string request = Soup.Form.encode ( - "api_option", "paste", - "api_dev_key", "67480801fa55fc0977f7561cf650a339", - "api_paste_code", paste_code, - "api_paste_name", paste_name, - "api_paste_private", paste_private, - "api_paste_expire_date", paste_expire_date, - "api_paste_format", paste_format); - - message.set_request ("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, request.data); - message.set_flags (Soup.MessageFlags.NO_REDIRECT); - - session.send_message (message); - - var output = (string) message.response_body.data; - link = output; - - if (Uri.parse_scheme (output) == null || message.status_code != 200) { - // A URI was not returned - return false; - } - - return true; - } - } -} - -public class Scratch.Plugins.Pastebin : Peas.ExtensionBase, Peas.Activatable { +public class Scratch.Plugins.Pastebin : Scratch.Plugins.PluginBase { GLib.MenuItem? menuitem = null; GLib.Menu? share_menu = null; - public Object object { owned get; construct; } Scratch.Services.Document? doc = null; - Scratch.Services.Interface plugins; const string ACTION_GROUP = "pastebin"; const string ACTION_PREFIX = ACTION_GROUP + "."; @@ -85,17 +36,24 @@ public class Scratch.Plugins.Pastebin : Peas.ExtensionBase, Peas.Activatable { {ACTION_SHOW, show_paste_bin_upload_dialog } }; - public void update_state () { + public Pastebin (PluginInfo info, Interface iface) { + base (info, iface); } - public void activate () { - plugins = (Scratch.Services.Interface) object; - - plugins.hook_document.connect ((doc) => { + ulong doc_hook_handler = 0; + ulong menu_hook_handler = 0; + protected override void activate_internal () { + doc_hook_handler = iface.hook_document.connect ((doc) => { this.doc = doc; }); - plugins.hook_share_menu.connect (on_hook_share_menu); + menu_hook_handler = iface.hook_share_menu.connect (on_hook_share_menu); + } + + protected override void deactivate_internal () { + remove_actions (); + iface.disconnect (menu_hook_handler); + iface.disconnect (doc_hook_handler); } void on_hook_share_menu (GLib.MenuModel menu) { @@ -112,7 +70,7 @@ public class Scratch.Plugins.Pastebin : Peas.ExtensionBase, Peas.Activatable { actions.add_action_entries (ACTION_ENTRIES, this); } - plugins.manager.window.insert_action_group (ACTION_GROUP, actions); + iface.manager.window.insert_action_group (ACTION_GROUP, actions); share_menu = (GLib.Menu) menu; menuitem = new GLib.MenuItem (_("Upload to Pastebin"), ACTION_PREFIX + ACTION_SHOW); share_menu.append_item (menuitem); @@ -131,22 +89,18 @@ public class Scratch.Plugins.Pastebin : Peas.ExtensionBase, Peas.Activatable { } } - plugins.manager.window.insert_action_group (ACTION_GROUP, null); + iface.manager.window.insert_action_group (ACTION_GROUP, null); } void show_paste_bin_upload_dialog () { - MainWindow window = plugins.manager.window; + MainWindow window = iface.manager.window; new Dialogs.PasteBinDialog (window, doc); } - - public void deactivate () { - remove_actions (); - } } -[ModuleInit] -public void peas_register_types (GLib.TypeModule module) { - var objmodule = module as Peas.ObjectModule; - objmodule.register_extension_type (typeof (Peas.Activatable), - typeof (Scratch.Plugins.Pastebin)); +public Scratch.Plugins.PluginBase module_init ( + Scratch.Plugins.PluginInfo info, + Scratch.Plugins.Interface iface +) { + return new Scratch.Plugins.Pastebin (info, iface); } diff --git a/plugins/pastebin/pastebin_dialog.vala b/plugins/pastebin/pastebin_dialog.vala index db4ad5d5be..0cd514b9e9 100644 --- a/plugins/pastebin/pastebin_dialog.vala +++ b/plugins/pastebin/pastebin_dialog.vala @@ -21,7 +21,53 @@ using Scratch.Services; namespace Scratch.Dialogs { + public class PasteBinSubmitter : GLib.Object { + public const string NEVER = "N"; + public const string TEN_MINUTES = "10M"; + public const string HOUR = "1H"; + public const string DAY = "1D"; + public const string MONTH = "1M"; + public const string PRIVATE = "1"; + public const string PUBLIC = "0"; + + + public static bool submit (out string link, string paste_code, string paste_name, + string paste_private, string paste_expire_date, + string paste_format) { + + if (paste_code.length == 0) { link = "No text to paste"; return false; } + + string api_url = "https://pastebin.com/api/api_post.php"; + + var session = new Soup.Session (); + var message = new Soup.Message ("POST", api_url); + + string request = Soup.Form.encode ( + "api_option", "paste", + "api_dev_key", "67480801fa55fc0977f7561cf650a339", + "api_paste_code", paste_code, + "api_paste_name", paste_name, + "api_paste_private", paste_private, + "api_paste_expire_date", paste_expire_date, + "api_paste_format", paste_format); + + message.set_request ("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, request.data); + message.set_flags (Soup.MessageFlags.NO_REDIRECT); + + session.send_message (message); + + var output = (string) message.response_body.data; + link = output; + + if (Uri.parse_scheme (output) == null || message.status_code != 200) { + // A URI was not returned + return false; + } + + return true; + } + } public class PasteBinDialog : Granite.Dialog { public string[,] languages = { @@ -457,18 +503,18 @@ namespace Scratch.Dialogs { string paste_code = this.doc.get_text (); string paste_name = name_entry.text; string paste_format = format_combo.get_active_id (); - string paste_private = private_check.get_active () == true ? PasteBin.PRIVATE : PasteBin.PUBLIC; + string paste_private = private_check.get_active () == true ? PasteBinSubmitter.PRIVATE : PasteBinSubmitter.PUBLIC; string paste_expire_date = expiry_combo.get_active_id (); - return PasteBin.submit (out link, paste_code, paste_name, paste_private, paste_expire_date, paste_format); + return PasteBinSubmitter.submit (out link, paste_code, paste_name, paste_private, paste_expire_date, paste_format); } private void populate_expiry_combo () { - expiry_combo.append (PasteBin.NEVER, _("Never")); - expiry_combo.append (PasteBin.TEN_MINUTES, _("Ten minutes")); - expiry_combo.append (PasteBin.HOUR, _("One hour")); - expiry_combo.append (PasteBin.DAY, _("One day")); - expiry_combo.append (PasteBin.MONTH, _("One month")); + expiry_combo.append (PasteBinSubmitter.NEVER, _("Never")); + expiry_combo.append (PasteBinSubmitter.TEN_MINUTES, _("Ten minutes")); + expiry_combo.append (PasteBinSubmitter.HOUR, _("One hour")); + expiry_combo.append (PasteBinSubmitter.DAY, _("One day")); + expiry_combo.append (PasteBinSubmitter.MONTH, _("One month")); } } } diff --git a/plugins/preserve-indent/preserve-indent.vala b/plugins/preserve-indent/preserve-indent.vala index 6f07e0c3d3..8204152e8a 100644 --- a/plugins/preserve-indent/preserve-indent.vala +++ b/plugins/preserve-indent/preserve-indent.vala @@ -1,8 +1,9 @@ // -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- /*** BEGIN LICENSE + Copyright (C) 2024 elementary, Inc. + 2015 James Morgan - Copyright (C) 2015 James Morgan This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3, as published by the Free Software Foundation. @@ -18,21 +19,21 @@ END LICENSE ***/ -public class Scratch.Plugins.PreserveIndent : Peas.ExtensionBase, Peas.Activatable { - - private Scratch.Services.Interface plugins; +public class Scratch.Plugins.PreserveIndent : Scratch.Plugins.PluginBase { private Gee.TreeSet documents; private Services.Document active_document; private int last_clipboard_indent_level = 0; private bool waiting_for_clipboard_text = false; - public Object object { owned get; construct; } + public PreserveIndent (PluginInfo info, Interface iface) { + base (info, iface); + } - public void activate () { + ulong doc_hook_handler = 0; + protected override void activate_internal () { this.documents = new Gee.TreeSet (); - plugins = (Scratch.Services.Interface) object; - plugins.hook_document.connect ((d) => { + doc_hook_handler = iface.hook_document.connect ((d) => { this.active_document = d; if (documents.add (d)) { @@ -48,11 +49,9 @@ public class Scratch.Plugins.PreserveIndent : Peas.ExtensionBase, Peas.Activatab }); } - public void deactivate () { + protected override void deactivate_internal () { this.documents.clear (); - } - - public void update_state () { + iface.disconnect (doc_hook_handler); } // determine how many characters precede a given iterator position @@ -241,9 +240,9 @@ public class Scratch.Plugins.PreserveIndent : Peas.ExtensionBase, Peas.Activatab } } -[ModuleInit] -public void peas_register_types (GLib.TypeModule module) { - var objmodule = module as Peas.ObjectModule; - objmodule.register_extension_type (typeof (Peas.Activatable), - typeof (Scratch.Plugins.PreserveIndent)); +public Scratch.Plugins.PluginBase module_init ( + Scratch.Plugins.PluginInfo info, + Scratch.Plugins.Interface iface +) { + return new Scratch.Plugins.PreserveIndent (info, iface); } diff --git a/plugins/spell/spell.vala b/plugins/spell/spell.vala index 6e90e60e2f..77eea72c29 100644 --- a/plugins/spell/spell.vala +++ b/plugins/spell/spell.vala @@ -1,6 +1,7 @@ -/* - * Copyright (C) 2011-2012 Mario Guerriero This program - * is free software: you can redistribute it and/or modify it under the +/* Copyright (C) 2024 elementary, Inc. + * 2011-2012 Mario Guerriero + * + * This program is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License version 3, as published by * the Free Software Foundation. * @@ -13,14 +14,9 @@ * with this program. If not, see */ -public class Scratch.Plugins.Spell: Peas.ExtensionBase, Peas.Activatable { - - Scratch.Services.Interface plugins; - +public class Scratch.Plugins.Spell: Scratch.Plugins.PluginBase { private GLib.Settings settings; - MainWindow window = null; - private string lang_dict; #if SPELLLEGACY @@ -29,12 +25,13 @@ public class Scratch.Plugins.Spell: Peas.ExtensionBase, Peas.Activatable { GtkSpell.Checker spell = null; #endif - public Object object {owned get; construct;} - - public void update_state () { + public Spell (PluginInfo info, Interface iface) { + base (info, iface); } - public void activate () { + ulong window_hook_handler = 0; + ulong doc_hook_handler = 0; + protected override void activate_internal () { settings = new GLib.Settings (Constants.PROJECT_NAME + ".plugins.spell"); // Restore the last dictionary used. @@ -42,8 +39,16 @@ public class Scratch.Plugins.Spell: Peas.ExtensionBase, Peas.Activatable { settings.changed.connect (settings_changed); - plugins = (Scratch.Services.Interface) object; - plugins.hook_document.connect ((d) => { + window_hook_handler = iface.hook_window.connect ((w) => { + if (window != null) { + return; + } + + window = w; + window.destroy.connect (save_settings); + }); + + doc_hook_handler = iface.hook_document.connect ((d) => { var view = d.source_view; // Create spell object @@ -92,8 +97,8 @@ public class Scratch.Plugins.Spell: Peas.ExtensionBase, Peas.Activatable { } #endif // Deactivate Spell checker when it is no longer needed - plugins.manager.extension_removed.connect ((info) => { - if (info.get_module_name () == "spell") + iface.manager.extension_removed.connect ((info) => { + if (info.module_name == "spell") spell.detach (); }); @@ -111,22 +116,16 @@ public class Scratch.Plugins.Spell: Peas.ExtensionBase, Peas.Activatable { spell.language_changed.connect ((lang_dict) => { this.lang_dict = lang_dict; }); - } }); - - plugins.hook_window.connect ((w) => { - if (window != null) { - return; - } - - window = w; - window.destroy.connect (save_settings); - }); - } - + protected override void deactivate_internal () { + save_settings (); + window.destroy.disconnect (save_settings); + iface.disconnect (window_hook_handler); + iface.disconnect (doc_hook_handler); + } private void language_changed_spell (Scratch.Widgets.SourceView view) { if (view.language != null) @@ -149,18 +148,11 @@ public class Scratch.Plugins.Spell: Peas.ExtensionBase, Peas.Activatable { settings.set_string ("language", lang_dict); } - public void deactivate () { - save_settings (); - window.destroy.disconnect (save_settings); - } - } -[ModuleInit] -public void peas_register_types (GLib.TypeModule module) { - var objmodule = module as Peas.ObjectModule; - objmodule.register_extension_type ( - typeof (Peas.Activatable), - typeof (Scratch.Plugins.Spell) - ); +public Scratch.Plugins.PluginBase module_init ( + Scratch.Plugins.PluginInfo info, + Scratch.Plugins.Interface iface +) { + return new Scratch.Plugins.Spell (info, iface); } diff --git a/plugins/vim-emulation/vim-emulation.vala b/plugins/vim-emulation/vim-emulation.vala index 32ec3c397a..1a6c5cfdcb 100644 --- a/plugins/vim-emulation/vim-emulation.vala +++ b/plugins/vim-emulation/vim-emulation.vala @@ -1,8 +1,9 @@ // -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- /*** BEGIN LICENSE + Copyright (C) 2024 elementary, Inc. + 2013 Mario Guerriero - Copyright (C) 2013 Mario Guerriero This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3, as published by the Free Software Foundation. @@ -18,7 +19,7 @@ END LICENSE ***/ -public class Scratch.Plugins.VimEmulation : Peas.ExtensionBase, Peas.Activatable { +public class Scratch.Plugins.VimEmulation : Scratch.Plugins.PluginBase { public enum Mode { COMMAND, INSERT, @@ -33,20 +34,17 @@ public class Scratch.Plugins.VimEmulation : Peas.ExtensionBase, Peas.Activatable Gee.TreeSet views; Scratch.Widgets.SourceView? view = null; - Scratch.Services.Interface plugins; - public Object object { owned get; construct; } + public VimEmulation (PluginInfo info, Interface iface) { + base (info, iface); + } construct { views = new Gee.TreeSet (); } - public void update_state () { - - } - - public void activate () { - plugins = (Scratch.Services.Interface) object; - plugins.hook_document.connect ((doc) => { + ulong doc_hook_handler = 0; + protected override void activate_internal () { + doc_hook_handler = iface.hook_document.connect ((doc) => { this.view = doc.source_view; this.view.key_press_event.disconnect (handle_key_press); this.view.key_press_event.connect (handle_key_press); @@ -54,10 +52,12 @@ public class Scratch.Plugins.VimEmulation : Peas.ExtensionBase, Peas.Activatable }); } - public void deactivate () { + protected override void deactivate_internal () { foreach (var v in views) { v.key_press_event.disconnect (handle_key_press); } + + iface.disconnect (doc_hook_handler); } private bool handle_key_press (Gdk.EventKey event) { @@ -295,9 +295,9 @@ public class Scratch.Plugins.VimEmulation : Peas.ExtensionBase, Peas.Activatable } } -[ModuleInit] -public void peas_register_types (GLib.TypeModule module) { - var objmodule = module as Peas.ObjectModule; - objmodule.register_extension_type (typeof (Peas.Activatable), - typeof (Scratch.Plugins.VimEmulation)); +public Scratch.Plugins.PluginBase module_init ( + Scratch.Plugins.PluginInfo info, + Scratch.Plugins.Interface iface +) { + return new Scratch.Plugins.VimEmulation (info, iface); } diff --git a/plugins/word-completion/plugin.vala b/plugins/word-completion/plugin.vala index d227d12a6a..45b92c6d33 100644 --- a/plugins/word-completion/plugin.vala +++ b/plugins/word-completion/plugin.vala @@ -1,5 +1,6 @@ /* - * Copyright (c) 2011 Lucas Baudin + * Copyright (C) 2024 elementary, Inc. + * 2011 Lucas Baudin * * This is a free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -18,16 +19,13 @@ * */ -public class Scratch.Plugins.Completion : Peas.ExtensionBase, Peas.Activatable { - public Object object { owned get; construct; } - +public class Scratch.Plugins.Completion : Scratch.Plugins.PluginBase { private List text_view_list = new List (); public Euclide.Completion.Parser parser {get; private set;} public Gtk.SourceView? current_view {get; private set;} public Scratch.Services.Document current_document {get; private set;} private MainWindow main_window; - private Scratch.Services.Interface plugins; private bool completion_in_progress = false; private const uint [] ACTIVATE_KEYS = { @@ -43,22 +41,25 @@ public class Scratch.Plugins.Completion : Peas.ExtensionBase, Peas.Activatable { private uint timeout_id = 0; - public void activate () { - plugins = (Scratch.Services.Interface) object; + public Completion (PluginInfo info, Interface iface) { + base (info, iface); + } + + ulong window_hook_handler = 0; + ulong doc_hook_handler = 0; + protected override void activate_internal () { parser = new Euclide.Completion.Parser (); - plugins.hook_window.connect ((w) => { + window_hook_handler = iface.hook_window.connect ((w) => { this.main_window = w; }); - plugins.hook_document.connect (on_new_source_view); + doc_hook_handler = iface.hook_document.connect (on_new_source_view); } - public void deactivate () { + protected override void deactivate_internal () { text_view_list.@foreach (cleanup); - } - - public void update_state () { - + iface.disconnect (window_hook_handler); + iface.disconnect (doc_hook_handler); } public void on_new_source_view (Scratch.Services.Document doc) { @@ -182,9 +183,9 @@ public class Scratch.Plugins.Completion : Peas.ExtensionBase, Peas.Activatable { } } -[ModuleInit] -public void peas_register_types (GLib.TypeModule module) { - var objmodule = module as Peas.ObjectModule; - objmodule.register_extension_type (typeof (Peas.Activatable), - typeof (Scratch.Plugins.Completion)); +public Scratch.Plugins.PluginBase module_init ( + Scratch.Plugins.PluginInfo info, + Scratch.Plugins.Interface iface +) { + return new Scratch.Plugins.Completion (info, iface); } diff --git a/src/Dialogs/PreferencesDialog.vala b/src/Dialogs/PreferencesDialog.vala index 96f2de4590..2e5f4b0b40 100644 --- a/src/Dialogs/PreferencesDialog.vala +++ b/src/Dialogs/PreferencesDialog.vala @@ -8,9 +8,9 @@ */ public class Scratch.Dialogs.Preferences : Granite.Dialog { - public Services.PluginsManager plugins { get; construct; } + public Scratch.Services.PluginsManager plugins { get; construct; } - public Preferences (Gtk.Window? parent, Services.PluginsManager plugins) { + public Preferences (Gtk.Window? parent, Scratch.Services.PluginsManager plugins) { Object ( title: _("Preferences"), transient_for: parent, @@ -146,7 +146,7 @@ public class Scratch.Dialogs.Preferences : Granite.Dialog { plugins.hook_preferences_dialog (this); - if (Peas.Engine.get_default ().get_plugin_list ().length () > 0) { + if (plugins.get_n_plugins () > 0) { var pbox = plugins.get_view (); pbox.vexpand = true; @@ -160,6 +160,11 @@ public class Scratch.Dialogs.Preferences : Granite.Dialog { close_button.clicked.connect (() => { destroy (); }); + + // Always start with Behaviour page + realize.connect (() => { + stack.visible_child_name = "behavior"; + }); } private class SettingSwitch : Gtk.Grid { diff --git a/src/Services/PluginManager.vala b/src/Services/PluginManager.vala index 26729a2400..644f15f09b 100644 --- a/src/Services/PluginManager.vala +++ b/src/Services/PluginManager.vala @@ -18,123 +18,355 @@ END LICENSE ***/ -namespace Scratch.Services { - public class Interface : GLib.Object { - - public PluginsManager manager; - - // Signals - public signal void hook_window (Scratch.MainWindow window); - public signal void hook_share_menu (GLib.MenuModel menu); - public signal void hook_toolbar (Scratch.HeaderBar toolbar); - public signal void hook_document (Scratch.Services.Document doc); - public signal void hook_preferences_dialog (Scratch.Dialogs.Preferences dialog); - public signal void hook_folder_item_change (File file, File? other_file, FileMonitorEvent event_type); - - public Scratch.TemplateManager template_manager { private set; get; } - - public Interface (PluginsManager manager) { - this.manager = manager; - - template_manager = new Scratch.TemplateManager (); - } +// namespace Scratch.Plugins { +public abstract class Scratch.Plugins.PluginBase : GLib.Object { + public PluginInfo plugin_info { get; construct; } + public Interface iface { get; construct; } + public bool is_active { get; set; } + + protected PluginBase (PluginInfo info, Interface iface) { + Object ( + plugin_info: info, + iface: iface + ); + } - public Document open_file (File file) { - var doc = new Document (manager.window.actions, file); - manager.window.open_document (doc); - return doc; + public void activate () { + if (is_active) { + return; } - public void close_document (Document doc) { - manager.window.close_document (doc); - } + is_active = true; + activate_internal (); } - public class PluginsManager : GLib.Object { - Peas.Engine engine; - Peas.ExtensionSet exts; - - string settings_field; - - public Interface plugin_iface { private set; public get; } + public void deactivate () { + if (!is_active) { + return; + } - public weak MainWindow window; + is_active = false; + deactivate_internal (); + } - // Signals - public signal void hook_window (Scratch.MainWindow window); - public signal void hook_share_menu (GLib.MenuModel menu); - public signal void hook_toolbar (Scratch.HeaderBar toolbar); - public signal void hook_document (Scratch.Services.Document doc); - public signal void hook_preferences_dialog (Scratch.Dialogs.Preferences dialog); - public signal void hook_folder_item_change (File file, File? other_file, FileMonitorEvent event_type); + protected abstract void activate_internal (); + protected abstract void deactivate_internal (); + public virtual void update_state () {} // Not currently used +} - public signal void extension_added (Peas.PluginInfo info); - public signal void extension_removed (Peas.PluginInfo info); +public struct Scratch.Plugins.PluginInfo { + string name; + string module_name; + string description; + string icon_name; +} - public PluginsManager (MainWindow window) { - this.window = window; +public class Scratch.Plugins.Interface : GLib.Object { + public Scratch.Services.PluginsManager manager; + // Signals + public signal void hook_window (Scratch.MainWindow window); + public signal void hook_share_menu (GLib.MenuModel menu); + public signal void hook_toolbar (Scratch.HeaderBar toolbar); + public signal void hook_document (Scratch.Services.Document doc); + public signal void hook_preferences_dialog (Scratch.Dialogs.Preferences dialog); + public signal void hook_folder_item_change (File file, File? other_file, FileMonitorEvent event_type); + + public Scratch.TemplateManager template_manager { private set; get; } + + public Interface (Scratch.Services.PluginsManager manager) { + this.manager = manager; + template_manager = new Scratch.TemplateManager (); + } +} - settings_field = "plugins-enabled"; +delegate Scratch.Plugins.PluginBase ModuleInitFunc ( + Scratch.Plugins.PluginInfo info, + Scratch.Plugins.Interface iface +); + +public class Scratch.Services.PluginsManager : GLib.Object { + public const string ACTIVE_PLUGINS_KEY = "plugins-enabled"; + public const string KEYFILE_FILE_EXTENSION = ".plugin"; + public const string MODULE_FILE_EXTENSION = ".so"; + + public Scratch.Plugins.Interface plugin_iface { private set; public get; } + public weak MainWindow window; + + // Signals + public signal void hook_window (Scratch.MainWindow window); + public signal void hook_share_menu (GLib.MenuModel menu); + public signal void hook_toolbar (Scratch.HeaderBar toolbar); + public signal void hook_document (Scratch.Services.Document doc); + public signal void hook_preferences_dialog (Scratch.Dialogs.Preferences dialog); + public signal void hook_folder_item_change (File file, File? other_file, FileMonitorEvent event_type); + + public signal void extension_added (); + public signal void extension_removed (Scratch.Plugins.PluginInfo info); + + Gee.HashMap plugin_hash; // all plugins + public Gee.HashSet active_plugin_set; //active plugin names + + public PluginsManager (MainWindow window) { + this.window = window; + plugin_iface = new Scratch.Plugins.Interface (this); + + /* From Files PluginManager construct */ + plugin_hash = new Gee.HashMap (); + active_plugin_set = new Gee.HashSet (); + // Code has only one plugin directory. + load_modules_from_dir (Constants.PLUGINDIR); + + // Connect managers signals to interface's signals + this.hook_window.connect ((w) => { + plugin_iface.hook_window (w); + }); + + this.hook_share_menu.connect ((m) => { + plugin_iface.hook_share_menu (m); + }); + + this.hook_toolbar.connect ((t) => { + plugin_iface.hook_toolbar (t); + }); + + this.hook_document.connect ((d) => { + plugin_iface.hook_document (d); + }); + + this.hook_preferences_dialog.connect ((d) => { + plugin_iface.hook_preferences_dialog (d); + }); + + this.hook_folder_item_change.connect ((source, dest, event) => { + plugin_iface.hook_folder_item_change (source, dest, event); + }); + + // Activate plugins according to setting + foreach (var module_name in settings.get_strv (ACTIVE_PLUGINS_KEY)) { + if (plugin_hash.has_key (module_name)) { + var plugin = plugin_hash.@get (module_name); + activate_plugin (plugin); + } + } + } - plugin_iface = new Interface (this); + private void activate_plugin (Scratch.Plugins.PluginBase plugin) { + var info = plugin.plugin_info; + if (!plugin.is_active) { + plugin.activate (); + active_plugin_set.add (info.module_name); + extension_added (); // Signals Window to run initial hook function + } + } - /* Let's init the engine */ - engine = Peas.Engine.get_default (); - engine.enable_loader ("python"); - engine.add_search_path (Constants.PLUGINDIR, null); - Scratch.settings.bind ("plugins-enabled", engine, "loaded-plugins", SettingsBindFlags.DEFAULT); + private void deactivate_plugin (Scratch.Plugins.PluginBase plugin) { + var info = plugin.plugin_info; + if (plugin.is_active) { + plugin.deactivate (); + active_plugin_set.remove (info.module_name); + extension_removed (info); + } + } - /* Our extension set */ - exts = new Peas.ExtensionSet (engine, typeof (Peas.Activatable), "object", plugin_iface, null); + private void update_active_plugin_settings () { + // For some reason using active_plugin_set.to_array () does not work (crashes) + // So construct the string array ourselves + string[] module_names = {}; + foreach (string module in active_plugin_set) { + module_names += module; + } - exts.extension_added.connect ((info, ext) => { - ((Peas.Activatable)ext).activate (); - extension_added (info); - }); + settings.set_strv (ACTIVE_PLUGINS_KEY, module_names); + } - exts.extension_removed.connect ((info, ext) => { - ((Peas.Activatable)ext).deactivate (); - extension_removed (info); - }); + private void load_modules_from_dir (string path) { + FileInfo info; + FileEnumerator enumerator; + try { + string attributes = FileAttribute.STANDARD_NAME + "," + + FileAttribute.STANDARD_TYPE; + var dir = GLib.File.new_for_path (path); + enumerator = dir.enumerate_children ( + attributes, + FileQueryInfoFlags.NONE + ); + + info = enumerator.next_file (); + while (info != null) { + if (info.get_file_type () == DIRECTORY) { + load_modules_from_dir (Path.build_filename (path, info.get_name ())); + } else { + string file_name = info.get_name (); + var plugin_file = dir.get_child_for_display_name (file_name); + if (file_name.has_suffix (KEYFILE_FILE_EXTENSION)) { + load_plugin_keyfile (plugin_file, path); + } + } + + info = enumerator.next_file (); + } + } catch (Error error) { + critical ("Error listing contents of folder '%s': %s", path, error.message); + } + } - exts.foreach (on_extension_foreach); + private bool load_module (File dir, Scratch.Plugins.PluginInfo info) { + if (plugin_hash.has_key (info.module_name)) { + warning ("plugin for %s already loaded. Not adding again", info.name); + return false; + } - // Connect managers signals to interface's signals - this.hook_window.connect ((w) => { - plugin_iface.hook_window (w); - }); + //TODO Add a File key in the same way that Files does so we do not + // have to construct the module path + var file_path = dir.get_path ().concat ( + Path.DIR_SEPARATOR_S, + "lib", + info.module_name, + MODULE_FILE_EXTENSION + ); + debug ("Loading plugin for %s", file_path); + + Module module = Module.open (file_path, ModuleFlags.LOCAL); + if (module == null) { + warning ( + "Failed to load module from path '%s': %s", + file_path, + Module.error () + ); + return false; + } - this.hook_share_menu.connect ((m) => { - plugin_iface.hook_share_menu (m); - }); + void* function; + if (!module.symbol ("module_init", out function)) { + warning ( + "Failed to find entry point function '%s' in '%s': %s", + "module_init", + file_path, + Module.error () + ); + return false; + } - this.hook_toolbar.connect ((t) => { - plugin_iface.hook_toolbar (t); - }); + unowned ModuleInitFunc module_init = (ModuleInitFunc) function; + assert (module_init != null); + + //TODO Reconsider making all plugins resident for Code + module.make_resident (); + Scratch.Plugins.PluginBase plug = module_init (info, plugin_iface); + debug ("Loaded module source: '%s'", module.name ()); + + if (plug != null) { + plugin_hash.set (info.module_name, plug); + plug.is_active = false; + // Plugins only become active via initial settings or preferences dialog + return true; + } else { + critical ("Module init failed for %s, it will not be available", module.name ()); + } - this.hook_document.connect ((d) => { - plugin_iface.hook_document (d); - }); + return false; + } - this.hook_preferences_dialog.connect ((d) => { - plugin_iface.hook_preferences_dialog (d); - }); + // Load the keyfile from specified location + private void load_plugin_keyfile (File keyfile_file, string parent) { + var keyfile = new KeyFile (); + var plugin_info = Scratch.Plugins.PluginInfo (); + try { + keyfile.load_from_file (keyfile_file.get_path (), KeyFileFlags.NONE); + plugin_info.name = keyfile.get_string ("Plugin", "Name"); + plugin_info.module_name = keyfile.get_string ("Plugin", "Module"); + plugin_info.description = keyfile.get_string ("Plugin", "Description"); + if (keyfile.has_key ("Plugin", "Icon")) { + plugin_info.icon_name = keyfile.get_string ("Plugin", "Icon"); + } else { + plugin_info.icon_name = "extension"; + } + // Should we expose the author(s)? + load_module (keyfile_file.get_parent (), plugin_info); + } catch (Error e) { + warning ("Couldn't open the keyfile '%s': %s", keyfile_file.get_path (), e.message); - this.hook_folder_item_change.connect ((source, dest, event) => { - plugin_iface.hook_folder_item_change (source, dest, event); - }); } + } - void on_extension_foreach (Peas.ExtensionSet set, Peas.PluginInfo info, Peas.Extension extension) { - ((Peas.Activatable)extension).activate (); + // Return an emulation of the libpeas-1.0 widget + public Gtk.Widget get_view () { + var list_box = new Gtk.ListBox (); + var scrolled_window = new Gtk.ScrolledWindow (null, null) { + hscrollbar_policy = NEVER, + vscrollbar_policy = AUTOMATIC, + max_content_height = 300, + child = list_box + }; + var frame = new Gtk.Frame (null) { + child = scrolled_window + }; + + foreach (var plugin in plugin_hash.values) { + var content = get_widget_for_plugin (plugin); + var row = new Gtk.ListBoxRow () { + child = content + }; + + list_box.add (row); } - public Gtk.Widget get_view () { - var view = new PeasGtk.PluginManager (engine); - var bottom_box = view.get_children ().nth_data (1); - bottom_box.no_show_all = true; - return view; - } + // Could use a TreeMap (sortable) + list_box.set_sort_func ((r1, r2) => { + return strcmp ( + r1.get_child ().get_data ("name"), + r2.get_child ().get_data ("name") + ); + }); + frame.show_all (); + return frame; + } + + private Gtk.Widget get_widget_for_plugin (Scratch.Plugins.PluginBase plugin) { + var info = plugin.plugin_info; + var content = new Gtk.Box (HORIZONTAL, 6); + var checkbox = new Gtk.CheckButton () { + valign = Gtk.Align.CENTER, + active = plugin.is_active, + margin_start = 6 + }; + + checkbox.toggled.connect (() => { + if (checkbox.active) { + activate_plugin (plugin); + } else { + deactivate_plugin (plugin); + } + + update_active_plugin_settings (); + }); + var image = new Gtk.Image.from_icon_name (info.icon_name, LARGE_TOOLBAR) { + valign = Gtk.Align.CENTER + }; + var description_box = new Gtk.Box (VERTICAL, 0); + var name_label = new Granite.HeaderLabel (info.name); + //TODO In Granite-7 we can use secondary text property but emulate for now + var description_label = new Gtk.Label (info.description) { + use_markup = true, + wrap = true, + xalign = 0, + margin_start = 6, + margin_bottom = 6 + }; + description_label.get_style_context ().add_class (Granite.STYLE_CLASS_SMALL_LABEL); + description_label.get_style_context ().add_class (Gtk.STYLE_CLASS_DIM_LABEL); + description_box.add (name_label); + description_box.add (description_label); + content.add (checkbox); + content.add (image); + content.add (description_box); + content.set_data ("name", info.name); + + return content; + } + + public uint get_n_plugins () { + return plugin_hash.size; } } diff --git a/src/codecore.deps b/src/codecore.deps index dcfdbb4614..b6cfd8dccc 100644 --- a/src/codecore.deps +++ b/src/codecore.deps @@ -2,7 +2,7 @@ codecore gtksourceview-4 gee-0.8 gobject-2.0 +gmodule-2.0 gio-2.0 gtk+-3.0 granite -libpeas-1.0