diff --git a/README.markdown b/README.markdown index fd42392..f21c550 100644 --- a/README.markdown +++ b/README.markdown @@ -60,12 +60,27 @@ To build GeanyPy you need the following dependencies: * Python 2.6+ and development files (I don't think Python 3 will work). * Geany 0.21+ and development files (from SVN) + +** For Gtk2 ** + * PyGTK 2.0 and development files +** For Gtk3 ** + +* GObject Introspection is needed [pygtk not needed.] + On Debian/Ubuntu, the (non-Geany) dependencies can be installed like this: - $ apt-get install python python-dev python-gtk2 python-gtk2-dev + $ sudo apt-get install python python-dev + +** For Gtk2 ** + + $ sudo apt-get install python-gtk2 python-gtk2-dev + +** For Gtk3 ** + $ sudo apt-get install python-gi python-gi-dev + See Geany's documentation/website for information on compiling it from the Subversion or Git repositories. @@ -90,4 +105,4 @@ far. I imagine 2.7 series will also work fine. Running on Windows ------------------ -See `README.win32` for more information. +See `README.win32` for more information. \ No newline at end of file diff --git a/configure.ac b/configure.ac index e603255..0b188b9 100644 --- a/configure.ac +++ b/configure.ac @@ -22,7 +22,7 @@ LT_INIT([disable-static]) # Check for headers, libraries and packages AC_CHECK_HEADERS([stdio.h string.h dlfcn.h]) PKG_CHECK_MODULES([GEANY], [geany >= 0.21]) -PKG_CHECK_MODULES([PYGTK], [pygtk-2.0]) +PKG_CHECK_MODULES([PYGOBJECT], [pygobject-3.0]) AX_PYTHON_DEVEL([>= '2.6']) AX_PYTHON_LIBRARY(,[AC_MSG_ERROR([Cannot determine location of the Python DSO])]) AC_SUBST([PYTHON]) @@ -41,3 +41,5 @@ AC_CONFIG_FILES([ plugins/Makefile ]) AC_OUTPUT +m4_pattern_allow([AM_PROG_AR]) +AM_PROG_AR \ No newline at end of file diff --git a/geany/__init__.py b/geany/__init__.py index 5412f8c..7ae38f2 100644 --- a/geany/__init__.py +++ b/geany/__init__.py @@ -8,6 +8,7 @@ """ import app +import bindings import console import dialogs import document @@ -67,5 +68,8 @@ # GObject to connect signal handlers on and which emits signals. signals = SignalManager() +# Initialize the keybindings manager +bindings.init() + import plugin from plugin import Plugin diff --git a/geany/console.py b/geany/console.py index 415e4df..807ed7e 100644 --- a/geany/console.py +++ b/geany/console.py @@ -37,6 +37,14 @@ # inside: GtkTextView is not a terminal. # The use case is: you have a python program, you create this widget, # and inspect your program interiors. +try: + from gi import pygtkcompat +except ImportError: + pygtkcompat = None + +if pygtkcompat is not None: + pygtkcompat.enable() + pygtkcompat.enable_gtk(version='3.0') import gtk import gtk.gdk as gdk @@ -160,7 +168,7 @@ def raw_input(self, ps=None): self.thaw_undo() self.__move_cursor_to(iter) - self.scroll_to_mark(self.cursor, 0.2) + self.scroll_to_mark(self.cursor, 0.2, use_align=False, xalign=0.5, yalign=0.5) self.in_raw_input = True @@ -177,7 +185,7 @@ def on_buf_mark_set(self, buffer, iter, mark): if iter.compare(self.__get_start()) >= 0 and \ iter.compare(self.__get_end()) <= 0: buffer.move_mark_by_name("cursor", iter) - self.scroll_to_mark(self.cursor, 0.2) + self.scroll_to_mark(self.cursor, 0.2, use_align=False, xalign=0.5, yalign=0.5) def __insert(self, iter, text): self.do_insert = True @@ -286,7 +294,7 @@ def do_key_press_event(self, event, parent_type): handled = False if not handled: - return parent_type.do_key_press_event(self, event) + return parent_type.do_key_press_event(self, event.key if hasattr(event, "key") else event) else: return True @@ -296,7 +304,7 @@ def __history(self, dir): if not new_text is None: self.__replace_line(new_text) self.__move_cursor(0) - self.scroll_to_mark(self.cursor, 0.2) + self.scroll_to_mark(self.cursor, 0.2, use_align=False, xalign=0.5, yalign=0.5) def __get_cursor(self): return self.buffer.get_iter_at_mark(self.cursor) @@ -343,7 +351,7 @@ def __delete_at_cursor(self, howmany): self.__delete(iter, end) def __get_width(self): - if not (self.flags() & gtk.REALIZED): + if not (self.get_realized()): return 80 layout = pango.Layout(self.get_pango_context()) letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" @@ -386,7 +394,7 @@ def __print_completions(self, completions): self.__insert(iter, "%s%s%s" % (self.ps, line_start, line_end)) iter.set_line_offset(len(self.ps) + len(line_start)) self.__move_cursor_to(iter) - self.scroll_to_mark(self.cursor, 0.2) + self.scroll_to_mark(self.cursor, 0.2, use_align=False, xalign=0.5, yalign=0.5) def __complete(self): text = self.__get_text(self.__get_start(), self.__get_cursor()) diff --git a/geany/loader.py b/geany/loader.py index 14b9b73..ae4e504 100644 --- a/geany/loader.py +++ b/geany/loader.py @@ -8,165 +8,159 @@ class PluginLoader(object): - plugins = {} - - def __init__(self, plugin_dirs): - - self.plugin_dirs = plugin_dirs - - self.available_plugins = [] - for plugin in self.iter_plugin_info(): - self.available_plugins.append(plugin) - - self.restore_loaded_plugins() - - - def update_loaded_plugins_file(self): - for path in self.plugin_dirs: - if os.path.isdir(path): - try: - state_file = os.path.join(path, '.loaded_plugins') - with open(state_file, 'w') as f: - for plugfn in self.plugins: - f.write("%s\n" % plugfn) - except IOError as err: - if err.errno == 13: #perms - pass - else: - raise - - - def restore_loaded_plugins(self): - loaded_plugins = [] - for path in reversed(self.plugin_dirs): - state_file = os.path.join(path, ".loaded_plugins") - if os.path.exists(state_file): - for line in open(state_file): - line = line.strip() - if line not in loaded_plugins: - loaded_plugins.append(line) - for filename in loaded_plugins: - self.load_plugin(filename) - - - def load_all_plugins(self): - - for plugin_info in self.iter_plugin_info(): - if plugin_filename.endswith('test.py'): # hack for testing - continue - plug = self.load_plugin(plugin_info.filename) - if plug: - print("Loaded plugin: %s" % plugin_info.filename) - print(" Name: %s v%s" % (plug.name, plug.version)) - print(" Desc: %s" % plug.description) - print(" Author: %s" % plug.author) - - - def unload_all_plugins(self): - - for plugin in self.plugins: - self.unload_plugin(plugin) - - - def reload_all_plugins(self): - - self.unload_all_plugins() - self.load_all_plugins() - - - def iter_plugin_info(self): - - for d in self.plugin_dirs: - if os.path.isdir(d): - for current_file in os.listdir(d): - #check inside folders inside the plugins dir so we can load .py files here as plugins - current_path=os.path.abspath(os.path.join(d, current_file)) - if os.path.isdir(current_path): - for plugin_folder_file in os.listdir(current_path): - if plugin_folder_file.endswith('.py'): - #loop around results if its fails to load will never reach yield - for p in self.load_plugin_info(current_path,plugin_folder_file): - yield p - - #not a sub directory so if it ends with .py lets just attempt to load it as a plugin - if current_file.endswith('.py'): - #loop around results if its fails to load will never reach yield - for p in self.load_plugin_info(d,current_file): - yield p - - def load_plugin_info(self,d,f): - filename = os.path.abspath(os.path.join(d, f)) - if filename.endswith("test.py"): - pass - text = open(filename).read() - module_name = os.path.basename(filename)[:-3] - try: - module = imp.load_source(module_name, filename) - except ImportError as exc: - print "Error: failed to import settings module ({})".format(exc) - module=None - if module: - for k, v in module.__dict__.iteritems(): - if k == geany.Plugin.__name__: - continue - try: - if issubclass(v, geany.Plugin): - inf = PluginInfo( - filename, - getattr(v, '__plugin_name__'), - getattr(v, '__plugin_version__', ''), - getattr(v, '__plugin_description__', ''), - getattr(v, '__plugin_author__', ''), - v) - yield inf - - except TypeError: - continue - - - def load_plugin(self, filename): - - for avail in self.available_plugins: - if avail.filename == filename: - inst = avail.cls() - self.plugins[filename] = inst - self.update_loaded_plugins_file() - geany.ui_utils.set_statusbar('GeanyPy: plugin activated: %s' % - inst.name, True) - return inst - - - def unload_plugin(self, filename): - - try: - plugin = self.plugins[filename] - name = plugin.name - plugin.cleanup() - del self.plugins[filename] - self.update_loaded_plugins_file() - geany.ui_utils.set_statusbar('GeanyPy: plugin deactivated: %s' % - name, True) - except KeyError: - print("Unable to unload plugin '%s': it's not loaded" % filename) - - - def reload_plugin(self, filename): - - if filename in self.plugins: - self.unload_plugin(filename) - self.load_plugin(filename) - - - def plugin_has_help(self, filename): - - for plugin_info in self.iter_plugin_info(): - if plugin_info.filename == filename: - return hasattr(plugin_info.cls, 'show_help') - - - def plugin_has_configure(self, filename): - - try: - return hasattr(self.plugins[filename], 'show_configure') - except KeyError: - return None + plugins = {} + + def __init__(self, plugin_dirs): + self.plugin_dirs = plugin_dirs + self.restore_loaded_plugins() + + + def update_loaded_plugins_file(self): + for path in self.plugin_dirs: + if os.path.isdir(path): + try: + state_file = os.path.join(path, '.loaded_plugins') + with open(state_file, 'w') as f: + for plugfn in self.plugins: + f.write("%s\n" % plugfn) + except IOError as err: + if err.errno == 13: #perms + pass + else: + raise + + + def restore_loaded_plugins(self): + loaded_plugins = [] + for path in reversed(self.plugin_dirs): + state_file = os.path.join(path, ".loaded_plugins") + if os.path.exists(state_file): + for line in open(state_file): + line = line.strip() + if line not in loaded_plugins: + loaded_plugins.append(line) + for filename in loaded_plugins: + self.load_plugin(filename) + + + def load_all_plugins(self): + + for plugin_info in self.iter_plugin_info(): + if plugin_filename.endswith('test.py'): # hack for testing + continue + plug = self.load_plugin(plugin_info.filename) + if plug: + print("Loaded plugin: %s" % plugin_info.filename) + print(" Name: %s v%s" % (plug.name, plug.version)) + print(" Desc: %s" % plug.description) + print(" Author: %s" % plug.author) + + + def unload_all_plugins(self): + + for plugin in self.plugins: + self.unload_plugin(plugin) + + + def reload_all_plugins(self): + + self.unload_all_plugins() + self.load_all_plugins() + + + def iter_plugin_info(self): + + for d in self.plugin_dirs: + if os.path.isdir(d): + for current_file in os.listdir(d): + #check inside folders inside the plugins dir so we can load .py files here as plugins + current_path=os.path.abspath(os.path.join(d, current_file)) + if os.path.isdir(current_path): + for plugin_folder_file in os.listdir(current_path): + if plugin_folder_file.endswith('.py'): + #loop around results if its fails to load will never reach yield + for p in self.load_plugin_info(current_path,plugin_folder_file): + yield p + + #not a sub directory so if it ends with .py lets just attempt to load it as a plugin + if current_file.endswith('.py'): + #loop around results if its fails to load will never reach yield + for p in self.load_plugin_info(d,current_file): + yield p + + def load_plugin_info(self,d,f): + filename = os.path.abspath(os.path.join(d, f)) + if filename.endswith("test.py"): + pass + text = open(filename).read() + module_name = os.path.basename(filename)[:-3] + try: + module = imp.load_source(module_name, filename) + except ImportError as exc: + print "Error: failed to import settings module ({})".format(exc) + module=None + if module: + for k, v in module.__dict__.iteritems(): + if k == geany.Plugin.__name__: + continue + try: + if issubclass(v, geany.Plugin): + inf = PluginInfo( + filename, + getattr(v, '__plugin_name__'), + getattr(v, '__plugin_version__', ''), + getattr(v, '__plugin_description__', ''), + getattr(v, '__plugin_author__', ''), + v) + yield inf + + except TypeError: + continue + + + def load_plugin(self, filename): + + for avail in self.iter_plugin_info(): + if avail.filename == filename: + inst = avail.cls() + self.plugins[filename] = inst + self.update_loaded_plugins_file() + geany.ui_utils.set_statusbar('GeanyPy: plugin activated: %s' % + inst.name, True) + return inst + + + def unload_plugin(self, filename): + + try: + plugin = self.plugins[filename] + name = plugin.name + plugin.cleanup() + del self.plugins[filename] + self.update_loaded_plugins_file() + geany.ui_utils.set_statusbar('GeanyPy: plugin deactivated: %s' % + name, True) + except KeyError: + print("Unable to unload plugin '%s': it's not loaded" % filename) + + + def reload_plugin(self, filename): + + if filename in self.plugins: + self.unload_plugin(filename) + self.load_plugin(filename) + + + def plugin_has_help(self, filename): + + for plugin_info in self.iter_plugin_info(): + if plugin_info.filename == filename: + return hasattr(plugin_info.cls, 'show_help') + + + def plugin_has_configure(self, filename): + + try: + return hasattr(self.plugins[filename], 'show_configure') + except KeyError: + return None \ No newline at end of file diff --git a/geany/manager.py b/geany/manager.py index 9c7e9a4..2a004c8 100644 --- a/geany/manager.py +++ b/geany/manager.py @@ -1,3 +1,12 @@ +try: + from gi import pygtkcompat +except ImportError: + pygtkcompat = None + +if pygtkcompat is not None: + pygtkcompat.enable() + pygtkcompat.enable_gtk(version='3.0') + import gtk import gobject import glib @@ -7,173 +16,179 @@ class PluginManager(gtk.Dialog): - def __init__(self, plugin_dirs=[]): - gtk.Dialog.__init__(self, title="Plugin Manager") - self.loader = PluginLoader(plugin_dirs) + def __init__(self, parent=None, plugin_dirs=[]): + super(PluginManager, self).__init__(title="Plugin Manager", parent=parent, flags=gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL) + self.loader = PluginLoader(plugin_dirs) - self.set_default_size(400, 450) - self.set_has_separator(True) - icon = self.render_icon(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_MENU) - self.set_icon(icon) + self.set_default_size(400, 450) + icon = self.render_icon(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_MENU) + self.set_icon(icon) - self.connect("response", lambda w,d: self.hide()) + self.connect("response", lambda w,d: self.hide()) - vbox = gtk.VBox(False, 12) - vbox.set_border_width(12) + vbox = gtk.VBox(False, 12) + vbox.set_border_width(12) - lbl = gtk.Label("Choose plugins to load or unload:") - lbl.set_alignment(0.0, 0.5) - vbox.pack_start(lbl, False, False, 0) + lbl = gtk.Label("Choose plugins to load or unload:") + lbl.set_alignment(0.0, 0.5) + vbox.pack_start(lbl, False, False, 0) - sw = gtk.ScrolledWindow() - sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - sw.set_shadow_type(gtk.SHADOW_ETCHED_IN) - vbox.pack_start(sw, True, True, 0) + sw = gtk.ScrolledWindow() + if hasattr(sw, 'set_hexpand'): + sw.set_hexpand(True) + sw.set_vexpand(True) + sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + sw.set_shadow_type(gtk.SHADOW_ETCHED_IN) + vbox.pack_start(sw, True, True, 0) - self.treeview = gtk.TreeView() - sw.add(self.treeview) + self.treeview = gtk.TreeView() + sw.add(self.treeview) - vbox.show_all() + vbox.show_all() - self.get_content_area().add(vbox) + self.get_content_area().add(vbox) - action_area = self.get_action_area() - action_area.set_spacing(0) - action_area.set_homogeneous(False) + action_area = self.get_action_area() + action_area.set_spacing(0) + action_area.set_homogeneous(False) - btn = gtk.Button(stock=gtk.STOCK_CLOSE) - btn.set_border_width(6) - btn.connect("clicked", lambda x: self.response(gtk.RESPONSE_CLOSE)) - action_area.pack_start(btn, False, True, 0) - btn.show() + btn = gtk.Button(stock=gtk.STOCK_CLOSE) + btn.set_border_width(6) + btn.connect("clicked", lambda x: self.response(gtk.RESPONSE_CLOSE)) + action_area.pack_start(btn, False, True, 0) + btn.show() - self.btn_help = gtk.Button(stock=gtk.STOCK_HELP) - self.btn_help.set_border_width(6) - self.btn_help.set_no_show_all(True) - action_area.pack_start(self.btn_help, False, True, 0) - action_area.set_child_secondary(self.btn_help, True) + self.btn_help = gtk.Button(stock=gtk.STOCK_HELP) + self.btn_help.set_border_width(6) + self.btn_help.set_no_show_all(True) + action_area.pack_start(self.btn_help, False, True, 0) + action_area.set_child_secondary(self.btn_help, True) - self.btn_prefs = gtk.Button(stock=gtk.STOCK_PREFERENCES) - self.btn_prefs.set_border_width(6) - self.btn_prefs.set_no_show_all(True) - action_area.pack_start(self.btn_prefs, False, True, 0) - action_area.set_child_secondary(self.btn_prefs, True) + self.btn_prefs = gtk.Button(stock=gtk.STOCK_PREFERENCES) + self.btn_prefs.set_border_width(6) + self.btn_prefs.set_no_show_all(True) + action_area.pack_start(self.btn_prefs, False, True, 0) + action_area.set_child_secondary(self.btn_prefs, True) - action_area.show() + action_area.show() - self.load_plugins_list() + self.load_plugins_list() - def on_help_button_clicked(self, button, treeview, model): - path = treeview.get_cursor()[0] - iter = model.get_iter(path) - filename = model.get_value(iter, 2) - for plugin in self.loader.available_plugins: - if plugin.filename == filename: - plugin.cls.show_help() - break - else: - print("Plugin does not support help function") + def on_help_button_clicked(self, button, treeview, model): + path = treeview.get_cursor()[0] + iter = model.get_iter(path) + filename = model.get_value(iter, 2) + for plugin in self.loader.available_plugins: + if plugin.filename == filename: + plugin.cls.show_help() + break + else: + print("Plugin does not support help function") - def on_preferences_button_clicked(self, button, treeview, model): - path = treeview.get_cursor()[0] - iter = model.get_iter(path) - filename = model.get_value(iter, 2) - try: - self.loader.plugins[filename].show_configure() - except KeyError: - print("Plugin is not loaded, can't run configure function") + def on_preferences_button_clicked(self, button, treeview, model): + path = treeview.get_cursor()[0] + iter = model.get_iter(path) + filename = model.get_value(iter, 2) + try: + self.loader.plugins[filename].show_configure() + except KeyError: + print("Plugin is not loaded, can't run configure function") - def activate_plugin(self, filename): - self.loader.load_plugin(filename) + def activate_plugin(self, filename): + self.loader.load_plugin(filename) - def deactivate_plugin(self, filename): - self.loader.unload_plugin(filename) + def deactivate_plugin(self, filename): + self.loader.unload_plugin(filename) + + def deactivate_all_plugins(self): + self.response(gtk.RESPONSE_CLOSE) + self.loader.unload_all_plugins() - def load_plugins_list(self): - liststore = gtk.ListStore(gobject.TYPE_BOOLEAN, str, str) + def load_plugins_list(self): + liststore = gtk.ListStore(gobject.TYPE_BOOLEAN, str, str) - self.btn_help.connect("clicked", - self.on_help_button_clicked, self.treeview, liststore) + self.btn_help.connect("clicked", + self.on_help_button_clicked, self.treeview, liststore) - self.btn_prefs.connect("clicked", - self.on_preferences_button_clicked, self.treeview, liststore) + self.btn_prefs.connect("clicked", + self.on_preferences_button_clicked, self.treeview, liststore) - self.treeview.set_model(liststore) - self.treeview.set_headers_visible(False) - self.treeview.set_grid_lines(True) + self.treeview.set_model(liststore) + self.treeview.set_headers_visible(False) + self.treeview.set_grid_lines(True) - check_renderer = gtk.CellRendererToggle() - check_renderer.set_radio(False) - check_renderer.connect('toggled', self.on_plugin_load_toggled, liststore) - text_renderer = gtk.CellRendererText() + check_renderer = gtk.CellRendererToggle() + check_renderer.set_radio(False) + check_renderer.connect('toggled', self.on_plugin_load_toggled, liststore) + text_renderer = gtk.CellRendererText() - check_column = gtk.TreeViewColumn(None, check_renderer, active=0) - text_column = gtk.TreeViewColumn(None, text_renderer, markup=1) + check_column = gtk.TreeViewColumn(None, check_renderer, active=0) + text_column = gtk.TreeViewColumn(None, text_renderer, markup=1) - self.treeview.append_column(check_column) - self.treeview.append_column(text_column) + self.treeview.append_column(check_column) + self.treeview.append_column(text_column) - self.treeview.connect('row-activated', - self.on_row_activated, check_renderer, liststore) - self.treeview.connect('cursor-changed', - self.on_selected_plugin_changed, liststore) + self.treeview.connect('row-activated', + self.on_row_activated, check_renderer, liststore) + self.treeview.connect('cursor-changed', + self.on_selected_plugin_changed, liststore) - self.load_sorted_plugins_info(liststore) + self.load_sorted_plugins_info(liststore) - def load_sorted_plugins_info(self, list_store): + def load_sorted_plugins_info(self, list_store): - plugin_info_list = list(self.loader.iter_plugin_info()) - #plugin_info_list.sort(key=lambda pi: pi[1]) + plugin_info_list = list(self.loader.iter_plugin_info()) + #plugin_info_list.sort(key=lambda pi: pi[1]) - for plugin_info in plugin_info_list: + for plugin_info in plugin_info_list: - lbl = str('%s %s\n%s\n' + - 'Author: %s\n' + - 'Filename: %s') % ( - glib.markup_escape_text(plugin_info.name), - glib.markup_escape_text(plugin_info.version), - glib.markup_escape_text(plugin_info.description), - glib.markup_escape_text(plugin_info.author), - glib.markup_escape_text(plugin_info.filename)) + lbl = str('%s %s\n%s\n' + + 'Author: %s\n' + + 'Filename: %s') % ( + glib.markup_escape_text(plugin_info.name), + glib.markup_escape_text(plugin_info.version), + glib.markup_escape_text(plugin_info.description), + glib.markup_escape_text(plugin_info.author), + glib.markup_escape_text(plugin_info.filename)) - loaded = plugin_info.filename in self.loader.plugins + loaded = plugin_info.filename in self.loader.plugins - list_store.append([loaded, lbl, plugin_info.filename]) + list_store.append([loaded, lbl, plugin_info.filename]) - def on_selected_plugin_changed(self, treeview, model): + def on_selected_plugin_changed(self, treeview, model): - path = treeview.get_cursor()[0] - iter = model.get_iter(path) - filename = model.get_value(iter, 2) - active = model.get_value(iter, 0) + path = treeview.get_cursor()[0] + iter = model.get_iter(path) + filename = model.get_value(iter, 2) + active = model.get_value(iter, 0) - if self.loader.plugin_has_configure(filename): - self.btn_prefs.set_visible(True) - else: - self.btn_prefs.set_visible(False) + if self.loader.plugin_has_configure(filename): + self.btn_prefs.set_visible(True) + else: + self.btn_prefs.set_visible(False) - if self.loader.plugin_has_help(filename): - self.btn_help.set_visible(True) - else: - self.btn_help.set_visible(False) + if self.loader.plugin_has_help(filename): + self.btn_help.set_visible(True) + else: + self.btn_help.set_visible(False) - def on_plugin_load_toggled(self, cell, path, model): - active = not cell.get_active() - iter = model.get_iter(path) - model.set_value(iter, 0, active) - if active: - self.activate_plugin(model.get_value(iter, 2)) - else: - self.deactivate_plugin(model.get_value(iter, 2)) + def on_plugin_load_toggled(self, cell, path, model): + active = not cell.get_active() + iter = model.get_iter(path) + model.set_value(iter, 0, active) + if active: + self.activate_plugin(model.get_value(iter, 2)) + else: + self.deactivate_plugin(model.get_value(iter, 2)) - def on_row_activated(self, tvw, path, view_col, cell, model): - self.on_plugin_load_toggled(cell, path, model) + def on_row_activated(self, tvw, path, view_col, cell, model): + self.on_plugin_load_toggled(cell, path, model) diff --git a/geany/signalmanager.py b/geany/signalmanager.py index 45a434e..16d7910 100644 --- a/geany/signalmanager.py +++ b/geany/signalmanager.py @@ -3,56 +3,62 @@ all signals on. The signals are emitted from the C code in signalmanager.c, where the Geany types get wrapped in PyObject types. """ +try: + from gi import pygtkcompat +except ImportError: + pygtkcompat = None +if pygtkcompat is not None: + pygtkcompat.enable() + pygtkcompat.enable_gtk(version='3.0') import gobject class SignalManager(gobject.GObject): - """ - Manages callback functions for events emitted by Geany's internal GObject. - """ - __gsignals__ = { - 'build-start': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, - ()), - 'document-activate': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, - (gobject.TYPE_PYOBJECT,)), - 'document-before-save': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, - (gobject.TYPE_PYOBJECT,)), - 'document-close': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, - (gobject.TYPE_PYOBJECT,)), - 'document-filetype-set': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, - (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)), - 'document-new': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, - (gobject.TYPE_PYOBJECT,)), - 'document-open': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, - (gobject.TYPE_PYOBJECT,)), - 'document-reload': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, - (gobject.TYPE_PYOBJECT,)), - 'document-save': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, - (gobject.TYPE_PYOBJECT,)), - 'editor-notify': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, - (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)), - 'geany-startup-complete': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, - ()), - 'project-close': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, - ()), - 'project-dialog-confirmed': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, - (gobject.TYPE_PYOBJECT,)), - 'project-dialog-open': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, - (gobject.TYPE_PYOBJECT,)), - 'project-dialog-close': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, - (gobject.TYPE_PYOBJECT,)), - 'project-open': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, - (gobject.TYPE_PYOBJECT,)), - 'project-save': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, - (gobject.TYPE_PYOBJECT,)), - 'update-editor-menu': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, - (gobject.TYPE_STRING, gobject.TYPE_INT, - gobject.TYPE_PYOBJECT)), - } # __gsignals__ + """ + Manages callback functions for events emitted by Geany's internal GObject. + """ + __gsignals__ = { + 'build-start': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + ()), + 'document-activate': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + 'document-before-save': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + 'document-close': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + 'document-filetype-set': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)), + 'document-new': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + 'document-open': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + 'document-reload': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + 'document-save': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + 'editor-notify': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, + (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)), + 'geany-startup-complete': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + ()), + 'project-close': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + ()), + 'project-dialog-confirmed': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + 'project-dialog-open': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + 'project-dialog-close': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + 'project-open': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + 'project-save': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + 'update-editor-menu': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_STRING, gobject.TYPE_INT, + gobject.TYPE_PYOBJECT)), + } # __gsignals__ - def __init__(self): - self.__gobject_init__() - -gobject.type_register(SignalManager) + def __init__(self): + super(SignalManager, self).__init__() +gobject.type_register(SignalManager) \ No newline at end of file diff --git a/m4/gtk.m4 b/m4/gtk.m4 new file mode 100644 index 0000000..1958be1 --- /dev/null +++ b/m4/gtk.m4 @@ -0,0 +1,26 @@ +dnl checks for the GTK version to build against (2 or 3) +dnl defines GP_GTK_PACKAGE (e.g. "gtk+-2.0"), GP_GTK_VERSION (e.g. "2.24") and +dnl GP_GTK_VERSION_MAJOR (e.g. "2"); and defines the GP_GTK3 AM conditional +AC_DEFUN([GP_CHECK_GTK_VERSION], +[ + AC_REQUIRE([AC_PROG_AWK]) + AC_REQUIRE([PKG_PROG_PKG_CONFIG]) + + _gtk_req=$(${PKG_CONFIG} --print-requires geany | ${AWK} '/^gtk\+-/{print}') + GP_GTK_PACKAGE=$(echo ${_gtk_req} | ${AWK} '{print $[]1}') + GP_GTK_VERSION=$(echo ${_gtk_req} | ${AWK} '{print $[]3}') + GP_GTK_VERSION_MAJOR=$(echo ${GP_GTK_VERSION} | cut -d. -f1) + AC_SUBST([GP_GTK_PACKAGE]) + AC_SUBST([GP_GTK_VERSION]) + AC_SUBST([GP_GTK_VERSION_MAJOR]) + + AM_CONDITIONAL([GP_GTK3], [test "x$GP_GTK_VERSION_MAJOR" = x3]) + +]) + +dnl executes $1 if GTK3 is used, and $2 otherwise +AC_DEFUN([GP_CHECK_GTK3], +[ + AC_REQUIRE([GP_CHECK_GTK_VERSION]) + AS_IF([test ${GP_GTK_VERSION_MAJOR} = 3],[$1],[$2]) +]) \ No newline at end of file diff --git a/plugins/console.py b/plugins/console.py index ee26ae3..d6bbfaf 100644 --- a/plugins/console.py +++ b/plugins/console.py @@ -1,3 +1,12 @@ +try: + from gi import pygtkcompat +except ImportError: + pygtkcompat = None + +if pygtkcompat is not None: + pygtkcompat.enable() + pygtkcompat.enable_gtk(version='3.0') + import os from ConfigParser import SafeConfigParser import geany @@ -9,305 +18,304 @@ WIDGET_STATES = [ gtk.STATE_NORMAL, gtk.STATE_ACTIVE, gtk.STATE_PRELIGHT, - gtk.STATE_SELECTED, gtk.STATE_INSENSITIVE ] + gtk.STATE_SELECTED, gtk.STATE_INSENSITIVE ] class ConsolePlugin(geany.Plugin): - __plugin_name__ = "Python Console" - __plugin_description__ = ("Installs a Python console that allows you " + - "to use the Geany API.") - __plugin_version__ = "0.01" - __plugin_author__ = "Matthew Brush " - - _font = "Monospace 9" - _bg = "#FFFFFF" - _fg = "#000000" - _banner = ("Geany Python Console\n You can access the Geany Python " + - "API by importing the `geany' module.\n") - _use_rl_completer = True - - _builder = None - - def __init__(self): - geany.Plugin.__init__(self) - self.load_config() - self.install_console() - - - def cleanup(self): - #self.save_config() - self.on_save_config_timeout() # do it now - self.uninstall_console() - - - def load_config(self): - self.cfg_path = os.path.join(geany.app.configdir, "plugins", "pyconsole.conf") - self.cfg = SafeConfigParser() - self.cfg.read(self.cfg_path) - - - def save_config(self): - gobject.idle_add(self.on_save_config_timeout) - - - def on_save_config_timeout(self, data=None): - self.cfg.write(open(self.cfg_path, 'w')) - return False - - - def install_console(self): - - # load general settings - self.banner = self.banner - self.use_rl_completer = self.use_rl_completer - - self.swin = gtk.ScrolledWindow() - self.swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS) - self.console = geany.console.Console(banner = self.banner, - use_rlcompleter = self.use_rl_completer) - self.console.connect("populate-popup", self.on_console_populate_popup) - - # apply appearance settings - self.font = self.font - self.bg = self.bg - self.fg = self.fg - - self.swin.add(self.console) - geany.main_widgets.message_window_notebook.append_page(self.swin, - gtk.Label("Python")) - - self.swin.show_all() - self.save_config() - - def uninstall_console(self): - self.swin.destroy() - - - def _get_font(self): - if self.cfg.has_section('appearances'): - if self.cfg.has_option('appearances', 'font'): - return self.cfg.get('appearances', 'font') - return self._font - def _set_font(self, font): - self._font = font - font_desc = pango.FontDescription(font) - self.console.modify_font(font_desc) - if not self.cfg.has_section('appearances'): - self.cfg.add_section('appearances') - self.cfg.set('appearances', 'font', self._font) - self.save_config() - font = property(_get_font, _set_font) - - - def _get_bg(self): - if self.cfg.has_section('appearances'): - if self.cfg.has_option('appearances', 'bg_color'): - return self.cfg.get('appearances', 'bg_color') - return self._bg - def _set_bg(self, bg): - self._bg = bg - color = gdk.color_parse(self._bg) - for state in WIDGET_STATES: - self.console.modify_bg(state, color) - self.console.modify_base(state, color) - if not self.cfg.has_section('appearances'): - self.cfg.add_section('appearances') - self.cfg.set('appearances', 'bg_color', self._bg) - self.save_config() - bg = property(_get_bg, _set_bg) - - - def _get_fg(self): - if self.cfg.has_section('appearances'): - if self.cfg.has_option('appearances', 'fg_color'): - return self.cfg.get('appearances', 'fg_color') - return self._fg - def _set_fg(self, fg): - self._fg = fg - color = gdk.color_parse(self._fg) - for state in WIDGET_STATES: - self.console.modify_fg(state, color) - self.console.modify_text(state, color) - if not self.cfg.has_section('appearances'): - self.cfg.add_section('appearances') - self.cfg.set('appearances', 'fg_color', self._fg) - self.save_config() - fg = property(_get_fg, _set_fg) - - - def _get_banner(self): - if self.cfg.has_section('general'): - if self.cfg.has_option('general', 'banner'): - return self.cfg.get('general', 'banner') - return self._banner - def _set_banner(self, banner): - self._banner = banner - if not self.cfg.has_section('general'): - self.cfg.add_section('general') - self.cfg.set('general', 'banner', self._banner) - self.save_config() - banner = property(_get_banner, _set_banner) - - - def _get_use_rl_completer(self): - if self.cfg.has_section('general'): - if self.cfg.has_option('general', 'use_rl_completer'): - return self.cfg.getboolean('general', 'use_rl_completer') - return self._use_rl_completer - def _set_use_rl_completer(self, use_rl_completer): - self._use_rl_completer = use_rl_completer - if not self.cfg.has_section('general'): - self.cfg.add_section('general') - self.cfg.set('general', 'use_rl_completer', - str(self._use_rl_completer).lower()) - self.save_config() - use_rl_completer = property(_get_use_rl_completer, _set_use_rl_completer) - - - def on_console_populate_popup(self, textview, menu, data=None): - item = gtk.SeparatorMenuItem() - item.show() - menu.append(item) - item = gtk.ImageMenuItem(stock_id=gtk.STOCK_PREFERENCES) - item.show() - menu.append(item) - item.connect("activate", lambda w,d=None: self.show_configure()) - - - def on_banner_changed(self, text_buf, data=None): - self.banner = text_buf.get_text(text_buf.get_start_iter(), text_buf.get_end_iter()) - - def on_use_rl_completer_toggled(self, chk_btn, data=None): - self.use_rl_completer = chk_btn.get_active() - - def on_font_changed(self, font_btn, data=None): - self.font = font_btn.get_font_name() - - def on_fg_color_changed(self, clr_btn, data=None): - self.fg = clr_btn.get_color().to_string() - - def on_bg_color_changed(self, clr_btn, data=None): - self.bg = clr_btn.get_color().to_string() - - - def show_configure(self): - dialog = gtk.Dialog("Configure Python Console", - geany.main_widgets.window, - gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, - (gtk.STOCK_CLOSE, gtk.RESPONSE_ACCEPT)) - - dialog.set_has_separator(True) - - content_area = dialog.get_content_area() - content_area.set_border_width(6) - - vbox = gtk.VBox(spacing=6) - vbox.set_border_width(6) - - lbl = gtk.Label() - lbl.set_use_markup(True) - lbl.set_markup("General") - - fra_general = gtk.Frame("") - fra_general.set_shadow_type(gtk.SHADOW_NONE) - fra_general.set_label_widget(lbl) - - al_general = gtk.Alignment(0.0, 0.0, 1.0, 1.0) - al_general.set_padding(0, 0, 12, 0) - fra_general.add(al_general) - - tbl = gtk.Table(3, 2, False) - tbl.set_row_spacings(6) - tbl.set_col_spacings(6) - tbl.set_border_width(6) - - lbl = gtk.Label("Banner:") - lbl.set_alignment(0.0, 0.0) - - tvw = gtk.TextView() - tvw.get_buffer().set_text(self.banner) - tvw.get_buffer().connect("changed", self.on_banner_changed) + __plugin_name__ = "Python Console" + __plugin_description__ = ("Installs a Python console that allows you " + + "to use the Geany API.") + __plugin_version__ = "0.01" + __plugin_author__ = "Matthew Brush " - swin = gtk.ScrolledWindow() - swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - swin.set_shadow_type(gtk.SHADOW_ETCHED_IN) - swin.add(tvw) + _font = "Monospace 9" + _bg = "#FFFFFF" + _fg = "#000000" + _banner = ("Geany Python Console\n You can access the Geany Python " + + "API by importing the `geany' module.\n") + _use_rl_completer = True + + _builder = None + + def __init__(self): + super(ConsolePlugin, self).__init__() + self.load_config() + self.install_console() + + + def cleanup(self): + #self.save_config() + self.on_save_config_timeout() # do it now + self.uninstall_console() + + + def load_config(self): + self.cfg_path = os.path.join(geany.app.configdir, "plugins", "pyconsole.conf") + self.cfg = SafeConfigParser() + self.cfg.read(self.cfg_path) + + + def save_config(self): + gobject.idle_add(self.on_save_config_timeout) + + + def on_save_config_timeout(self, data=None): + self.cfg.write(open(self.cfg_path, 'w')) + return False + + + def install_console(self): + + # load general settings + self.banner = self.banner + self.use_rl_completer = self.use_rl_completer + + self.swin = gtk.ScrolledWindow() + self.swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS) + self.console = geany.console.Console(banner = self.banner, + use_rlcompleter = self.use_rl_completer) + self.console.connect("populate-popup", self.on_console_populate_popup) + + # apply appearance settings + self.font = self.font + self.bg = self.bg + self.fg = self.fg + + self.swin.add(self.console) + geany.main_widgets.message_window_notebook.append_page(self.swin, + gtk.Label("Python")) + + self.swin.show_all() + self.save_config() + + def uninstall_console(self): + self.swin.destroy() + + + def _get_font(self): + if self.cfg.has_section('appearances'): + if self.cfg.has_option('appearances', 'font'): + return self.cfg.get('appearances', 'font') + return self._font + def _set_font(self, font): + self._font = font + font_desc = pango.FontDescription(font) + self.console.modify_font(font_desc) + if not self.cfg.has_section('appearances'): + self.cfg.add_section('appearances') + self.cfg.set('appearances', 'font', self._font) + self.save_config() + font = property(_get_font, _set_font) + + + def _get_bg(self): + if self.cfg.has_section('appearances'): + if self.cfg.has_option('appearances', 'bg_color'): + return self.cfg.get('appearances', 'bg_color') + return self._bg + def _set_bg(self, bg): + self._bg = bg + color = gdk.color_parse(self._bg) + for state in WIDGET_STATES: + self.console.modify_bg(state, color) + self.console.modify_base(state, color) + if not self.cfg.has_section('appearances'): + self.cfg.add_section('appearances') + self.cfg.set('appearances', 'bg_color', self._bg) + self.save_config() + bg = property(_get_bg, _set_bg) + + + def _get_fg(self): + if self.cfg.has_section('appearances'): + if self.cfg.has_option('appearances', 'fg_color'): + return self.cfg.get('appearances', 'fg_color') + return self._fg + def _set_fg(self, fg): + self._fg = fg + color = gdk.color_parse(self._fg) + for state in WIDGET_STATES: + self.console.modify_fg(state, color) + self.console.modify_text(state, color) + if not self.cfg.has_section('appearances'): + self.cfg.add_section('appearances') + self.cfg.set('appearances', 'fg_color', self._fg) + self.save_config() + fg = property(_get_fg, _set_fg) + + + def _get_banner(self): + if self.cfg.has_section('general'): + if self.cfg.has_option('general', 'banner'): + return self.cfg.get('general', 'banner') + return self._banner + def _set_banner(self, banner): + self._banner = banner + if not self.cfg.has_section('general'): + self.cfg.add_section('general') + self.cfg.set('general', 'banner', self._banner) + self.save_config() + banner = property(_get_banner, _set_banner) + + + def _get_use_rl_completer(self): + if self.cfg.has_section('general'): + if self.cfg.has_option('general', 'use_rl_completer'): + return self.cfg.getboolean('general', 'use_rl_completer') + return self._use_rl_completer + def _set_use_rl_completer(self, use_rl_completer): + self._use_rl_completer = use_rl_completer + if not self.cfg.has_section('general'): + self.cfg.add_section('general') + self.cfg.set('general', 'use_rl_completer', + str(self._use_rl_completer).lower()) + self.save_config() + use_rl_completer = property(_get_use_rl_completer, _set_use_rl_completer) + + + def on_console_populate_popup(self, textview, menu, data=None): + item = gtk.SeparatorMenuItem() + item.show() + menu.append(item) + item = gtk.ImageMenuItem(stock_id=gtk.STOCK_PREFERENCES) + item.show() + menu.append(item) + item.connect("activate", lambda w,d=None: self.show_configure()) + + + def on_banner_changed(self, text_buf, data=None): + self.banner = text_buf.get_text(text_buf.get_start_iter(), text_buf.get_end_iter()) + + def on_use_rl_completer_toggled(self, chk_btn, data=None): + self.use_rl_completer = chk_btn.get_active() + + def on_font_changed(self, font_btn, data=None): + self.font = font_btn.get_font_name() + + def on_fg_color_changed(self, clr_btn, data=None): + self.fg = clr_btn.get_color().to_string() + + def on_bg_color_changed(self, clr_btn, data=None): + self.bg = clr_btn.get_color().to_string() + + + def show_configure(self): + dialog = gtk.Dialog("Configure Python Console", + geany.main_widgets.window, + gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, + (gtk.STOCK_CLOSE, gtk.RESPONSE_ACCEPT)) + + #dialog.set_has_separator(True) + + content_area = dialog.get_content_area() + content_area.set_border_width(6) + + vbox = gtk.VBox(spacing=6) + vbox.set_border_width(6) + + lbl = gtk.Label() + lbl.set_use_markup(True) + lbl.set_markup("General") + + fra_general = gtk.Frame() + fra_general.set_shadow_type(gtk.SHADOW_NONE) + fra_general.set_label_widget(lbl) + + al_general = gtk.Alignment(0.0, 0.0, 1.0, 1.0) + al_general.set_padding(0, 0, 12, 0) + fra_general.add(al_general) + + tbl = gtk.Table(3, 2, False) + tbl.set_row_spacings(6) + tbl.set_col_spacings(6) + tbl.set_border_width(6) + + lbl = gtk.Label("Banner:") + lbl.set_alignment(0.0, 0.0) + + tvw = gtk.TextView() + tvw.get_buffer().set_text(self.banner) + tvw.get_buffer().connect("changed", self.on_banner_changed) - tbl.attach(lbl, 0, 1, 0, 1, gtk.FILL, gtk.FILL, 0, 0) - tbl.attach(swin, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.EXPAND | gtk.FILL, 0, 0) + swin = gtk.ScrolledWindow() + swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + swin.set_shadow_type(gtk.SHADOW_ETCHED_IN) + swin.add(tvw) - lbl = gtk.Label("") - lbl.set_alignment(0.0, 0.5) + tbl.attach(lbl, 0, 1, 0, 1, gtk.FILL, gtk.FILL, 0, 0) + tbl.attach(swin, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.EXPAND | gtk.FILL, 0, 0) - check = gtk.CheckButton("Use Readline") - if self.use_rl_completer: - check.set_active(True) - check.connect("toggled", self.on_use_rl_completer_toggled) + lbl = gtk.Label("") + lbl.set_alignment(0.0, 0.5) - tbl.attach(lbl, 0, 1, 1, 2, gtk.FILL, gtk.FILL, 0, 0) - tbl.attach(check, 1, 2, 1, 2, gtk.FILL, gtk.FILL, 0, 0) + check = gtk.CheckButton("Use Readline") + if self.use_rl_completer: + check.set_active(True) + check.connect("toggled", self.on_use_rl_completer_toggled) - lbl = gtk.Label("") - lbl.set_alignment(0.0, 0.5) - lbl.set_use_markup(True) - lbl.set_markup('' + - 'Note: General settings will be applied when console is reloaded.' + - '') - tbl.attach(lbl, 0, 2, 2, 3, gtk.FILL, gtk.FILL, 0, 0) + tbl.attach(lbl, 0, 1, 1, 2, gtk.FILL, gtk.FILL, 0, 0) + tbl.attach(check, 1, 2, 1, 2, gtk.FILL, gtk.FILL, 0, 0) - al_general.add(tbl) + lbl = gtk.Label("") + lbl.set_alignment(0.0, 0.5) + lbl.set_use_markup(True) + lbl.set_markup('' + + 'Note: General settings will be applied when console is reloaded.' + + '') + tbl.attach(lbl, 0, 2, 2, 3, gtk.FILL, gtk.FILL, 0, 0) - lbl = gtk.Label() - lbl.set_use_markup(True) - lbl.set_markup("Appearances") + al_general.add(tbl) - fra_appearances = gtk.Frame("") - fra_appearances.set_shadow_type(gtk.SHADOW_NONE) - fra_appearances.set_label_widget(lbl) + lbl = gtk.Label() + lbl.set_use_markup(True) + lbl.set_markup("Appearances") - al_appearances = gtk.Alignment(0.0, 0.0, 1.0, 1.0) - al_appearances.set_padding(0, 0, 12, 0) - fra_appearances.add(al_appearances) + fra_appearances = gtk.Frame() + fra_appearances.set_shadow_type(gtk.SHADOW_NONE) + fra_appearances.set_label_widget(lbl) - tbl = gtk.Table(3, 2, False) - tbl.set_row_spacings(6) - tbl.set_col_spacings(6) - tbl.set_border_width(6) + al_appearances = gtk.Alignment(0.0, 0.0, 1.0, 1.0) + al_appearances.set_padding(0, 0, 12, 0) + fra_appearances.add(al_appearances) - lbl = gtk.Label("Font:") - lbl.set_alignment(0.0, 0.5) + tbl = gtk.Table(3, 2, False) + tbl.set_row_spacings(6) + tbl.set_col_spacings(6) + tbl.set_border_width(6) - btn = gtk.FontButton(self.font) - btn.connect("font-set", self.on_font_changed) + lbl = gtk.Label("Font:") + lbl.set_alignment(0.0, 0.5) - tbl.attach(lbl, 0, 1, 0, 1, gtk.FILL, gtk.FILL, 0, 0) - tbl.attach(btn, 1, 2, 0, 1, gtk.FILL | gtk.EXPAND, gtk.FILL, 0, 0) + btn = gtk.FontButton(self.font) + btn.connect("font-set", self.on_font_changed) - lbl = gtk.Label("FG Color:") - lbl.set_alignment(0.0, 0.5) + tbl.attach(lbl, 0, 1, 0, 1, gtk.FILL, gtk.FILL, 0, 0) + tbl.attach(btn, 1, 2, 0, 1, gtk.FILL | gtk.EXPAND, gtk.FILL, 0, 0) - btn = gtk.ColorButton(gdk.color_parse(self.fg)) - btn.connect("color-set", self.on_fg_color_changed) + lbl = gtk.Label("FG Color:") + lbl.set_alignment(0.0, 0.5) + btn = gtk.ColorButton(color=gdk.color_parse(self.fg)) + btn.connect("color-set", self.on_fg_color_changed) - tbl.attach(lbl, 0, 1, 1, 2, gtk.FILL, gtk.FILL, 0, 0) - tbl.attach(btn, 1, 2, 1, 2, gtk.FILL | gtk.EXPAND, gtk.FILL, 0, 0) + tbl.attach(lbl, 0, 1, 1, 2, gtk.FILL, gtk.FILL, 0, 0) + tbl.attach(btn, 1, 2, 1, 2, gtk.FILL | gtk.EXPAND, gtk.FILL, 0, 0) - lbl = gtk.Label("BG Color:") - lbl.set_alignment(0.0, 0.5) + lbl = gtk.Label("BG Color:") + lbl.set_alignment(0.0, 0.5) - btn = gtk.ColorButton(gdk.color_parse(self.bg)) - btn.connect("color-set", self.on_bg_color_changed) + btn = gtk.ColorButton(color=gdk.color_parse(self.bg)) + btn.connect("color-set", self.on_bg_color_changed) - tbl.attach(lbl, 0, 1, 2, 3, gtk.FILL, gtk.FILL, 0, 0) - tbl.attach(btn, 1, 2, 2, 3, gtk.FILL | gtk.EXPAND, gtk.FILL, 0, 0) + tbl.attach(lbl, 0, 1, 2, 3, gtk.FILL, gtk.FILL, 0, 0) + tbl.attach(btn, 1, 2, 2, 3, gtk.FILL | gtk.EXPAND, gtk.FILL, 0, 0) - al_appearances.add(tbl) + al_appearances.add(tbl) - vbox.pack_start(fra_general, True, True, 0) - vbox.pack_start(fra_appearances, False, True, 0) - content_area.pack_start(vbox, True, True, 0) - content_area.show_all() + vbox.pack_start(fra_general, True, True, 0) + vbox.pack_start(fra_appearances, False, True, 0) + content_area.pack_start(vbox, True, True, 0) + content_area.show_all() - dialog.run() - dialog.destroy() + dialog.run() + dialog.destroy() \ No newline at end of file diff --git a/plugins/demo.py b/plugins/demo.py index 77ef0d0..8b8a091 100644 --- a/plugins/demo.py +++ b/plugins/demo.py @@ -9,10 +9,10 @@ class DemoPlugin(geany.Plugin): __plugin_author__ = "Matthew Brush " def __init__(self): - geany.Plugin.__init__(self) + super(DemoPlugin, self).__init__() print("Demo plugin initializing") doc = geany.document.new_file() doc.editor.scintilla.set_text("Hello from the Demo plugin") def cleanup(self): - print("Demo plugin cleaning up") + print("Demo plugin cleaning up") \ No newline at end of file diff --git a/plugins/hello.py b/plugins/hello.py index dfa7d27..d6153b8 100644 --- a/plugins/hello.py +++ b/plugins/hello.py @@ -1,3 +1,11 @@ +try: + from gi import pygtkcompat +except ImportError: + pygtkcompat = None + +if pygtkcompat is not None: + pygtkcompat.enable() + pygtkcompat.enable_gtk(version='3.0') import gtk import geany @@ -18,4 +26,4 @@ def cleanup(self): self.menu_item.destroy() def on_hello_item_clicked(widget, data): - geany.dialogs.show_msgbox("Hello World") + geany.dialogs.show_msgbox("Hello World") \ No newline at end of file diff --git a/src/Makefile.am b/src/Makefile.am index 06a258d..d926ea2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,13 +2,14 @@ geanyplugin_LTLIBRARIES = geanypy.la geanyplugindir = $(libdir)/geany geanypy_la_LDFLAGS = -module -avoid-version -Wl,--export-dynamic -geanypy_la_CPPFLAGS = @GEANY_CFLAGS@ @PYGTK_CFLAGS@ @PYTHON_CPPFLAGS@ \ +geanypy_la_CPPFLAGS = @GEANY_CFLAGS@ @PYGOBJECT_CFLAGS@ @PYTHON_CPPFLAGS@ \ -DGEANYPY_PYTHON_DIR="\"$(libdir)/geany/geanypy\"" \ -DGEANYPY_PLUGIN_DIR="\"$(datadir)/geany/geanypy/plugins\"" \ -UHAVE_CONFIG_H -geanypy_la_LIBADD = @GEANY_LIBS@ @PYGTK_LIBS@ @PYTHON_LDFLAGS@ \ +geanypy_la_LIBADD = @GEANY_LIBS@ @PYGOBJECT_LIBS@ @PYTHON_LDFLAGS@ \ @PYTHON_EXTRA_LIBS@ @PYTHON_EXTRA_LDFLAGS@ geanypy_la_SOURCES = geanypy-app.c \ + geanypy-bindings.c \ geanypy-dialogs.c \ geanypy-document.c geanypy-document.h \ geanypy-editor.c geanypy-editor.h \ @@ -32,4 +33,4 @@ geanypy_la_SOURCES = geanypy-app.c \ geanypy-search.c \ geanypy-signalmanager.c geanypy-signalmanager.h \ geanypy-templates.c \ - geanypy-uiutils.c geanypy-uiutils.h + geanypy-uiutils.c geanypy-uiutils.h \ No newline at end of file diff --git a/src/geanypy-bindings.c b/src/geanypy-bindings.c new file mode 100644 index 0000000..5506faf --- /dev/null +++ b/src/geanypy-bindings.c @@ -0,0 +1,236 @@ +#include "geanypy.h" + + +#define KB_MAX 256 +#define KB_LABEL_MAX 255 + +typedef struct +{ + gchar *kg_name; + GeanyKeyGroup *group; + GeanyKeyBinding *bindings[KB_MAX] ; + PyObject *callbacks[KB_MAX]; + PyObject *user_data[KB_MAX]; + gchar labels[KB_MAX][KB_LABEL_MAX]; + gboolean slots[KB_MAX]; + gboolean registered[KB_MAX]; + gboolean initialized; +} KbManager; + + +static KbManager manager = { NULL }; + + +static gchar *name_to_key(const gchar *name) +{ + gchar *ptr, *ret = g_strdup(name); + for (ptr = ret; *ptr != '\0'; ptr++) { + if (!isalnum(*ptr)) + *ptr = '_'; + } + if (strlen(ret) && !isalpha(ret[0])) + ret[0] = '_'; + return ret; +} + + +static uint next_key_id(gboolean *found) +{ + uint i; + for (i = 0; i < KB_MAX; i++) + { + if (!manager.slots[i]) + { + if (found) + *found = TRUE; + return i; + } + } + if (found) + *found = FALSE; + return 0; +} + + +static void on_keybinding_activate(guint key_id) +{ + PyObject *args; + + g_return_if_fail(key_id < KB_MAX); + + if (manager.slots[key_id]) + { + g_return_if_fail(manager.callbacks[key_id]); + g_return_if_fail(PyCallable_Check(manager.callbacks[key_id])); + + if (manager.user_data[key_id]) + args = Py_BuildValue("(iO)", key_id, manager.user_data[key_id]); + else + args = Py_BuildValue("(i)", key_id); + + if (!args) + { + g_warning("Unable to build Python arguments."); + if (PyErr_Occurred()) + PyErr_Print(); + return; + } + + (void) PyObject_CallObject(manager.callbacks[key_id], args); + Py_DECREF(args); + } +} + + +PyObject * kb_manager_init(PyObject *module) +{ + + if (manager.initialized) + Py_RETURN_TRUE; + + manager.kg_name = name_to_key(geany_plugin->info->name /* FIXME: use plugin name */); + manager.group = plugin_set_key_group(geany_plugin, manager.kg_name, KB_MAX, + (GeanyKeyGroupCallback) on_keybinding_activate); + + if (manager.group) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + + +#if 0 +PyObject * kb_manager_finalize(PyObject *module) +{ + uint i; + for (i = 0; i < KB_MAX; i++) + { + Py_XDECREF(manager.callbacks[i]); + Py_XDECREF(manager.user_data[i]); + g_free(manager.labels[i]); + } + g_free(manager.kg_name); + Py_RETURN_NONE; +} +#endif + + +PyObject * kb_manager_register_binding(PyObject *module, PyObject *args) +{ + guint id; + gboolean found_id; + gchar *key_name; + const gchar *name, *label; + PyObject *pCallback, *pData = NULL; + + if (!PyArg_ParseTuple(args, "ssO|O", &name, &label, &pCallback, &pData)) + { + PyErr_SetString(PyExc_ValueError, "unable to parse arguments"); + return NULL; + } + + found_id = FALSE; + id = next_key_id(&found_id); + if (!found_id) + { + PyErr_SetString(PyExc_RuntimeError, "no free keybindings left"); + return NULL; + } + + if (!PyCallable_Check(pCallback)) + { + PyErr_SetString(PyExc_ValueError, "callback function is not callable"); + return NULL; + } + + /* TODO: need to check name, label, callback? */ + + snprintf(manager.labels[id], KB_LABEL_MAX, "%s: %s", name, label); + + if (!manager.registered[id]) + { + key_name = g_strdup_printf("%s_%d", manager.kg_name, id); + manager.bindings[id] = keybindings_set_item(manager.group, + (gsize) id, + NULL, + 0, + 0, + key_name, + manager.labels[id], + NULL); + g_free(key_name); + + if (manager.bindings[id]) + manager.registered[id] = TRUE; + else + { + PyErr_SetString(PyExc_RuntimeError, "unable to register keybinding"); + return NULL; + } + } + + Py_INCREF(pCallback); + manager.callbacks[id] = pCallback; + + Py_XINCREF(pData); + manager.user_data[id] = pData; + + manager.slots[id] = TRUE; + + return PyInt_FromLong((long) id); +} + + +PyObject * kb_manager_unregister_binding(PyObject *module, PyObject *args) +{ + guint id; + + if (!PyArg_ParseTuple(args, "I", &id)) + { + PyErr_SetString(PyExc_ValueError, "expecting a single int argument"); + return NULL; + } + + if (id >= KB_MAX) + { + gchar *msg = g_strdup_printf("id is out of range (0-%d)", KB_MAX); + PyErr_SetString(PyExc_IndexError, msg); + g_free(msg); + return NULL; + } + + if (!manager.slots[id]) + { + PyErr_SetString(PyExc_IndexError, "id is not registered"); + return NULL; + } + + Py_XDECREF(manager.callbacks[id]); + Py_XDECREF(manager.user_data[id]); + manager.callbacks[id] = NULL; + manager.user_data[id] = NULL; + manager.slots[id] = FALSE; + manager.labels[id][0] = '\0'; + + Py_RETURN_NONE; +} + + +static PyMethodDef KbManagerModule_methods[] = { + { "init", (PyCFunction) kb_manager_init, METH_NOARGS, + "Initialize the keybindings manager." }, + { "register_binding", (PyCFunction) kb_manager_register_binding, + METH_VARARGS, "Register a callback function for a keybinding event." }, + { "unregister_binding", (PyCFunction) kb_manager_unregister_binding, + METH_VARARGS, "Unregister a callback function by id." }, + { NULL } +}; + + +PyMODINIT_FUNC initbindings(void) +{ + PyObject *m; + + m = Py_InitModule3("bindings", KbManagerModule_methods, + "Keybindings management."); +} \ No newline at end of file diff --git a/src/geanypy-encoding.c b/src/geanypy-encoding.c index 8ffa5fa..a1fc21b 100644 --- a/src/geanypy-encoding.c +++ b/src/geanypy-encoding.c @@ -1,94 +1,6 @@ #include "geanypy.h" -static void -Encoding_dealloc(Encoding *self) -{ - g_return_if_fail(self != NULL); - self->ob_type->tp_free((PyObject *) self); -} - - -static int -Encoding_init(Encoding *self) -{ - g_return_val_if_fail(self != NULL, -1); - self->encoding = NULL; - return 0; -} - - -static PyObject * -Encoding_get_property(Encoding *self, const gchar *prop_name) -{ - g_return_val_if_fail(self != NULL, NULL); - g_return_val_if_fail(prop_name != NULL, NULL); - - if (!self->encoding) - { - PyErr_SetString(PyExc_RuntimeError, - "Encoding instance not initialized properly"); - return NULL; - } - - if (g_str_equal(prop_name, "charset") && self->encoding->charset) - return PyString_FromString(self->encoding->charset); - else if (g_str_equal(prop_name, "group")) - return PyInt_FromLong((glong) self->encoding->group); - else if (g_str_equal(prop_name, "idx")) - return PyInt_FromLong((glong) self->encoding->idx); - else if (g_str_equal(prop_name, "name") && self->encoding->name) - return PyString_FromString(self->encoding->name); - else if (g_str_equal(prop_name, "order")) - return PyInt_FromLong((glong) self->encoding->order); - - Py_RETURN_NONE; -} -GEANYPY_PROPS_READONLY(Encoding); - - -static PyGetSetDef Encoding_getseters[] = { - GEANYPY_GETSETDEF(Encoding, "charset", - "String representation of the encoding, ex. 'ISO-8859-3'."), - GEANYPY_GETSETDEF(Encoding, "group", - "Internally used member for grouping (see encoding.GROUP_* constants)."), - GEANYPY_GETSETDEF(Encoding, "idx", - "The index of the encoding, (see encoding.* constants, not encoding.GROUP_*)."), - GEANYPY_GETSETDEF(Encoding, "name", - "Translatable and descriptive name of the encoding, ex 'South European'."), - GEANYPY_GETSETDEF(Encoding, "order", - "Internally used member for grouping."), - { NULL } -}; - - -PyTypeObject EncodingType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ - "geany.encoding.Encoding", /* tp_name */ - sizeof(Encoding), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor) Encoding_dealloc, /* tp_dealloc */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "Wrapper around a GeanyEncoding structure.", /* tp_doc */ - 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ - Encoding_getseters, /* tp_getset */ - 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ - (initproc) Encoding_init, /* tp_init */ - 0, 0, /* tp_alloc - tp_new */ -}; - - -Encoding *Encoding_create_new_from_geany_encoding(GeanyEncoding *enc) -{ - Encoding *self; - self = (Encoding *) PyObject_CallObject((PyObject *) &EncodingType, NULL); - self->encoding = enc; - return self; -} - - static PyObject * Encodings_convert_to_utf8(PyObject *module, PyObject *args, PyObject *kwargs) { @@ -156,17 +68,6 @@ Encodings_get_charset_from_index(PyObject *module, PyObject *args, PyObject *kwa } -static const gchar *encoding_groups[] = { - "GROUP_NONE", - "GROUP_WEST_EUROPEAN", - "GROUP_EAST_EUROPEAN", - "GROUP_EAST_ASIAN", - "GROUP_ASIAN", - "GROUP_MIDDLE_EASTERN", - "GROUP_UNICODE" -}; - - static const gchar *encoding_names[] = { "ISO_8859_1", "ISO_8859_2", @@ -292,19 +193,9 @@ initencoding(void) int i; PyObject *m; - EncodingType.tp_new = PyType_GenericNew; - if (PyType_Ready(&EncodingType) < 0) - return; - m = Py_InitModule3("encoding", EncodingsModule_methods, "Encoding conversion functions."); - Py_INCREF(&EncodingType); - PyModule_AddObject(m, "Encoding", (PyObject *) &EncodingType); - for (i = 0; i < GEANY_ENCODINGS_MAX; i++) PyModule_AddIntConstant(m, encoding_names[i], (glong) i); - - for (i = 0; i < GEANY_ENCODING_GROUPS_MAX; i++) - PyModule_AddIntConstant(m, encoding_groups[i], (glong) i); } diff --git a/src/geanypy-encoding.h b/src/geanypy-encoding.h index 1c586c1..caea0a1 100644 --- a/src/geanypy-encoding.h +++ b/src/geanypy-encoding.h @@ -1,14 +1,4 @@ #ifndef GEANYPY_ENCODING_H__ #define GEANYPY_ENCODING_H__ -PyTypeObject EncodingType; - -typedef struct -{ - PyObject_HEAD - GeanyEncoding *encoding; -} Encoding; - -Encoding *Encoding_create_new_from_geany_encoding(GeanyEncoding *enc); - #endif /* GEANYPY_ENCODING_H__ */ diff --git a/src/geanypy-plugin.c b/src/geanypy-plugin.c index 48bf98f..c96d52f 100644 --- a/src/geanypy-plugin.c +++ b/src/geanypy-plugin.c @@ -43,6 +43,7 @@ static SignalManager *signal_manager = NULL; /* Forward declarations to prevent compiler warnings. */ PyMODINIT_FUNC initapp(void); +PyMODINIT_FUNC initbindings(void); PyMODINIT_FUNC initdialogs(void); PyMODINIT_FUNC initdocument(void); PyMODINIT_FUNC initeditor(void); @@ -81,9 +82,12 @@ GeanyPy_start_interpreter(void) #endif Py_Initialize(); - + #if GTK_CHECK_VERSION(3, 0, 0) + PySys_SetArgv(0, "[]"); + #endif /* Import the C modules */ initapp(); + initbindings(); initdialogs(); initdocument(); initeditor(); @@ -150,7 +154,6 @@ GeanyPy_init_manager(const gchar *dir) gchar *sys_plugin_dir = NULL; g_return_if_fail(dir != NULL); - module = PyImport_ImportModule("geany.manager"); if (module == NULL) { @@ -194,11 +197,11 @@ GeanyPy_init_manager(const gchar *dir) if (sys_plugin_dir) { g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "System plugins: %s", sys_plugin_dir); - args = Py_BuildValue("([s, s])", sys_plugin_dir, dir); + args = Py_BuildValue("(O, [s, s])", pygobject_new(G_OBJECT(geany_data->main_widgets->window)), sys_plugin_dir, dir); g_free(sys_plugin_dir); } else - args = Py_BuildValue("([s])", dir); + args = Py_BuildValue("(O, [s])", pygobject_new(G_OBJECT(geany_data->main_widgets->window)), dir); manager = PyObject_CallObject(man, args); if (PyErr_Occurred()) @@ -265,7 +268,7 @@ plugin_init(GeanyData *data) loader_item = gtk_menu_item_new_with_label(_("Python Plugin Manager")); gtk_widget_set_sensitive(loader_item, plugin_dir != NULL); - gtk_menu_append(GTK_MENU(geany->main_widgets->tools_menu), loader_item); + gtk_menu_shell_append(GTK_MENU_SHELL(geany->main_widgets->tools_menu), loader_item); gtk_widget_show(loader_item); g_signal_connect(loader_item, "activate", G_CALLBACK(on_python_plugin_loader_activate), NULL); @@ -274,6 +277,17 @@ plugin_init(GeanyData *data) G_MODULE_EXPORT void plugin_cleanup(void) { + PyObject* deactivate_all_plugins = PyObject_GetAttrString(manager, + "deactivate_all_plugins"); + if (deactivate_all_plugins != NULL) + { + PyObject* r = PyObject_CallObject(deactivate_all_plugins, NULL); + if (r) + Py_DECREF(r); + Py_DECREF(deactivate_all_plugins); + + } + signal_manager_free(signal_manager); Py_XDECREF(manager); GeanyPy_stop_interpreter(); diff --git a/src/geanypy-uiutils.c b/src/geanypy-uiutils.c index 58f63cb..4d2718e 100644 --- a/src/geanypy-uiutils.c +++ b/src/geanypy-uiutils.c @@ -3,7 +3,7 @@ #define GOB_CHECK(pyobj, arg) \ { \ - if (!pyobj || pyobj == Py_None || !pygobject_check(pyobj, PyGObject_Type)) \ + if (!pyobj || pyobj == Py_None || !pygobject_check(pyobj, PyGobject_Type)) \ { \ PyErr_SetString(PyExc_ValueError, \ "argument " #arg " must inherit from a gobject.GObject type"); \ @@ -23,7 +23,7 @@ } -static PyTypeObject *PyGObject_Type = NULL; +static PyTypeObject *PyGobject_Type = NULL; static PyObject * @@ -123,8 +123,8 @@ UiUtils_combo_box_add_to_history(PyObject *module, PyObject *args, PyObject *kwa { GOB_CHECK(py_cbo, 1); widget = pygobject_get(py_cbo); - GOB_TYPE_CHECK(widget, GTK_TYPE_COMBO_BOX_ENTRY, 1); - ui_combo_box_add_to_history(GTK_COMBO_BOX_ENTRY(widget), text, hist_len); + GOB_TYPE_CHECK(widget, GTK_TYPE_COMBO_BOX_TEXT, 1); + ui_combo_box_add_to_history(GTK_COMBO_BOX_TEXT(widget), text, hist_len); } Py_RETURN_NONE; @@ -418,18 +418,20 @@ static PyMethodDef UiUtilsModule_methods[] = { PyMODINIT_FUNC initui_utils(void) { - PyObject *m; - - init_pygobject(); - init_pygtk(); - m = PyImport_ImportModule("gobject"); - - if (m) - { - PyGObject_Type = (PyTypeObject *) PyObject_GetAttrString(m, "GObject"); - Py_XDECREF(m); - } - + PyObject *m; + #if GTK_CHECK_VERSION(3, 0, 0) + pygobject_init(-1, -1, -1); + m = PyImport_ImportModule("gi._gobject"); + #else + init_pygobject(); + init_pygtk(); + m = PyImport_ImportModule("gobject"); + #endif + if (m) + { + PyGobject_Type = (PyTypeObject *) PyObject_GetAttrString(m, "_PyGObject_API"); + Py_XDECREF(m); + } InterfacePrefsType.tp_new = PyType_GenericNew; if (PyType_Ready(&InterfacePrefsType) < 0) return; @@ -446,4 +448,4 @@ PyMODINIT_FUNC initui_utils(void) Py_INCREF(&MainWidgetsType); PyModule_AddObject(m, "MainWidgets", (PyObject *) &MainWidgetsType); -} +} \ No newline at end of file diff --git a/src/geanypy.h b/src/geanypy.h index 0029b70..f198213 100644 --- a/src/geanypy.h +++ b/src/geanypy.h @@ -77,12 +77,13 @@ extern "C" { #include #include -#ifndef GEANYPY_WINDOWS +#ifdef GEANYPY_WINDOWS /* Used with the results of `pkg-config --cflags pygtk-2.0` */ -# include -#else +#include /* On windows the path of pygtk.h is directly an include dir */ -# include +#endif +#if !GTK_CHECK_VERSION(3, 0, 0) +#include #endif #ifndef GTK