summary refs log blame commit diff stats
path: root/plugins/wmpa/xchat-plugin.h
blob: ee189ffe9511f3635122386712839f36bc3b461e (plain) (tree)















































































































































































































































































































































































                                                                                                           
/* You can distribute this header with your plugins for easy compilation */
#ifndef XCHAT_PLUGIN_H
#define XCHAT_PLUGIN_H

#define VER_STRING _T("1.0.2 (BETA)")

#include "stdafx.h"
#include <windows.h>
#include <time.h>
#include <tchar.h>

#define XCHAT_IFACE_MAJOR     1
#define XCHAT_IFACE_MINOR     9
#define XCHAT_IFACE_MICRO     11
#define XCHAT_IFACE_VERSION   ((XCHAT_IFACE_MAJOR * 10000) + \
                              (XCHAT_IFACE_MINOR * 100) + \
                              (XCHAT_IFACE_MICRO))

#define XCHAT_PRI_HIGHEST  127
#define XCHAT_PRI_HIGH     64
#define XCHAT_PRI_NORM     0
#define XCHAT_PRI_LOW      (-64)
#define XCHAT_PRI_LOWEST   (-128)

#define XCHAT_FD_READ      1
#define XCHAT_FD_WRITE     2
#define XCHAT_FD_EXCEPTION 4
#define XCHAT_FD_NOTSOCKET 8

#define XCHAT_EAT_NONE     0                                   /* pass it on through! */
#define XCHAT_EAT_XCHAT    1                                   /* don't let xchat see this event */
#define XCHAT_EAT_PLUGIN   2                                   /* don't let other plugins see this event */
#define XCHAT_EAT_ALL      (XCHAT_EAT_XCHAT|XCHAT_EAT_PLUGIN)  /* don't let anything see this event */

#ifdef __cplusplus
extern "C" {
#endif


   typedef struct _xchat_plugin xchat_plugin;
   typedef struct _xchat_list xchat_list;
   typedef struct _xchat_hook xchat_hook;
#ifndef PLUGIN_C
   typedef struct _xchat_context xchat_context;
#endif

#ifndef PLUGIN_C
   struct _xchat_plugin {
      /* these are only used on win32 */
      xchat_hook *(*xchat_hook_command) (xchat_plugin *ph,
                                         const char *name,
                                         int pri,
                                         int (*callback) (char *word[], char *word_eol[], void *user_data),
                                         const char *help_text,
                                         void *userdata);
      xchat_hook *(*xchat_hook_server) (xchat_plugin *ph,
                                        const char *name,
                                        int pri,
                                        int (*callback) (char *word[], char *word_eol[], void *user_data),
                                        void *userdata);
      xchat_hook *(*xchat_hook_print) (xchat_plugin *ph,
                                       const char *name,
                                       int pri,
                                       int (*callback) (char *word[], void *user_data),
                                       void *userdata);
      xchat_hook *(*xchat_hook_timer) (xchat_plugin *ph,
                                       int timeout,
                                       int (*callback) (void *user_data),
                                       void *userdata);
      xchat_hook *(*xchat_hook_fd) (xchat_plugin *ph,
                                    int fd,
                                    int flags,
                                    int (*callback) (int fd, int flags, void *user_data),
                                    void *userdata);
      void *(*xchat_unhook) (xchat_plugin *ph,
                             xchat_hook *hook);
      void (*xchat_print) (xchat_plugin *ph,
                           const char *text);
      void (*xchat_printf) (xchat_plugin *ph,
                            const char *format, ...);
      void (*xchat_command) (xchat_plugin *ph,
                             const char *command);
      void (*xchat_commandf) (xchat_plugin *ph,
                              const char *format, ...);
      int (*xchat_nickcmp) (xchat_plugin *ph,
                            const char *s1,
                            const char *s2);
      int (*xchat_set_context) (xchat_plugin *ph,
                                xchat_context *ctx);
      xchat_context *(*xchat_find_context) (xchat_plugin *ph,
                                            const char *servname,
                                            const char *channel);
      xchat_context *(*xchat_get_context) (xchat_plugin *ph);
      const char *(*xchat_get_info) (xchat_plugin *ph,
                                     const char *id);
      int (*xchat_get_prefs) (xchat_plugin *ph,
                              const char *name,
                              const char **string,
                              int *integer);
      xchat_list * (*xchat_list_get) (xchat_plugin *ph,
                                      const char *name);
      void (*xchat_list_free) (xchat_plugin *ph,
                               xchat_list *xlist);
      const char * const * (*xchat_list_fields) (xchat_plugin *ph,
                                                 const char *name);
      int (*xchat_list_next) (xchat_plugin *ph,
                              xchat_list *xlist);
      const char * (*xchat_list_str) (xchat_plugin *ph,
                                      xchat_list *xlist,
                                      const char *name);
      int (*xchat_list_int) (xchat_plugin *ph,
                             xchat_list *xlist,
                             const char *name);
      void * (*xchat_plugingui_add) (xchat_plugin *ph,
                                     const char *filename,
                                     const char *name,
                                     const char *desc,
                                     const char *version,
                                     char *reserved);
      void (*xchat_plugingui_remove) (xchat_plugin *ph,
                                      void *handle);
      int (*xchat_emit_print) (xchat_plugin *ph,
                               const char *event_name, ...);
      int (*xchat_read_fd) (xchat_plugin *ph,
                            void *src,
                            char *buf,
                            int *len);
      time_t (*xchat_list_time) (xchat_plugin *ph,
                                 xchat_list *xlist,
                                 const char *name);
      char *(*xchat_gettext) (xchat_plugin *ph,
                              const char *msgid);
      void (*xchat_send_modes) (xchat_plugin *ph,
                                const char **targets,
                                int ntargets,
                                int modes_per_line,
                                char sign,
                                char mode);
      char *(*xchat_strip) (xchat_plugin *ph,
                            const char *str,
                            int len,
                            int flags);
      void (*xchat_free) (xchat_plugin *ph,
                          void *ptr);
   };
#endif


   xchat_hook *
   xchat_hook_command (xchat_plugin *ph,
                       const char *name,
                       int pri,
                       int (*callback) (char *word[], char *word_eol[], void *user_data),
                       const char *help_text,
                       void *userdata);

   xchat_hook *
   xchat_hook_server (xchat_plugin *ph,
                      const char *name,
                      int pri,
                      int (*callback) (char *word[], char *word_eol[], void *user_data),
                      void *userdata);

   xchat_hook *
   xchat_hook_print (xchat_plugin *ph,
                     const char *name,
                     int pri,
                     int (*callback) (char *word[], void *user_data),
                     void *userdata);

   xchat_hook *
   xchat_hook_timer (xchat_plugin *ph,
                     int timeout,
                     int (*callback) (void *user_data),
                     void *userdata);

   xchat_hook *
   xchat_hook_fd (xchat_plugin *ph,
                  int fd,
                  int flags,
                  int (*callback) (int fd, int flags, void *user_data),
                  void *userdata);

   void *
   xchat_unhook (xchat_plugin *ph,
                 xchat_hook *hook);

   void
   xchat_print (xchat_plugin *ph,
                const char *text);

   void
   xchat_printf (xchat_plugin *ph,
                 const char *format, ...);

   void
   xchat_command (xchat_plugin *ph,
                  const char *command);

   void
   xchat_commandf (xchat_plugin *ph,
                   const char *format, ...);

   int
   xchat_nickcmp (xchat_plugin *ph,
                  const char *s1,
                  const char *s2);

   int
   xchat_set_context (xchat_plugin *ph,
                      xchat_context *ctx);

   xchat_context *
   xchat_find_context (xchat_plugin *ph,
                       const char *servname,
                       const char *channel);

   xchat_context *
   xchat_get_context (xchat_plugin *ph);

   const char *
   xchat_get_info (xchat_plugin *ph,
                   const char *id);

   int
   xchat_get_prefs (xchat_plugin *ph,
                    const char *name,
                    const char **string,
                    int *integer);

   xchat_list *
   xchat_list_get (xchat_plugin *ph,
                   const char *name);

   void
   xchat_list_free (xchat_plugin *ph,
                    xchat_list *xlist);

   const char * const *
   xchat_list_fields (xchat_plugin *ph,
                      const char *name);

   int
   xchat_list_next (xchat_plugin *ph,
                    xchat_list *xlist);

   const char *
   xchat_list_str (xchat_plugin *ph,
                   xchat_list *xlist,
                   const char *name);

   int
   xchat_list_int (xchat_plugin *ph,
                   xchat_list *xlist,
                   const char *name);

   time_t
   xchat_list_time (xchat_plugin *ph,
                    xchat_list *xlist,
                    const char *name);

   void *
   xchat_plugingui_add (xchat_plugin *ph,
                        const char *filename,
                        const char *name,
                        const char *desc,
                        const char *version,
                        char *reserved);

   void
   xchat_plugingui_remove (xchat_plugin *ph,
                           void *handle);

   int
   xchat_emit_print (xchat_plugin *ph,
                     const char *event_name, ...);

   char *
   xchat_gettext (xchat_plugin *ph,
                  const char *msgid);

   void
   xchat_send_modes (xchat_plugin *ph,
                     const char **targets,
                     int ntargets,
                     int modes_per_line,
                     char sign,
                     char mode);

   char *
   xchat_strip (xchat_plugin *ph,
                const char *str,
                int len,
                int flags);

   void
   xchat_free (xchat_plugin *ph,
               void *ptr);

#if !defined(PLUGIN_C) && defined(WIN32)
#ifndef XCHAT_PLUGIN_HANDLE
#define XCHAT_PLUGIN_HANDLE (ph)
#endif
#define xchat_hook_command ((XCHAT_PLUGIN_HANDLE)->xchat_hook_command)
#define xchat_hook_server ((XCHAT_PLUGIN_HANDLE)->xchat_hook_server)
#define xchat_hook_print ((XCHAT_PLUGIN_HANDLE)->xchat_hook_print)
#define xchat_hook_timer ((XCHAT_PLUGIN_HANDLE)->xchat_hook_timer)
#define xchat_hook_fd ((XCHAT_PLUGIN_HANDLE)->xchat_hook_fd)
#define xchat_unhook ((XCHAT_PLUGIN_HANDLE)->xchat_unhook)
#define xchat_print ((XCHAT_PLUGIN_HANDLE)->xchat_print)
#define xchat_printf ((XCHAT_PLUGIN_HANDLE)->xchat_printf)
#define xchat_command ((XCHAT_PLUGIN_HANDLE)->xchat_command)
#define xchat_commandf ((XCHAT_PLUGIN_HANDLE)->xchat_commandf)
#define xchat_nickcmp ((XCHAT_PLUGIN_HANDLE)->xchat_nickcmp)
#define xchat_set_context ((XCHAT_PLUGIN_HANDLE)->xchat_set_context)
#define xchat_find_context ((XCHAT_PLUGIN_HANDLE)->xchat_find_context)
#define xchat_get_context ((XCHAT_PLUGIN_HANDLE)->xchat_get_context)
#define xchat_get_info ((XCHAT_PLUGIN_HANDLE)->xchat_get_info)
#define xchat_get_prefs ((XCHAT_PLUGIN_HANDLE)->xchat_get_prefs)
#define xchat_list_get ((XCHAT_PLUGIN_HANDLE)->xchat_list_get)
#define xchat_list_free ((XCHAT_PLUGIN_HANDLE)->xchat_list_free)
#define xchat_list_fields ((XCHAT_PLUGIN_HANDLE)->xchat_list_fields)
#define xchat_list_str ((XCHAT_PLUGIN_HANDLE)->xchat_list_str)
#define xchat_list_int ((XCHAT_PLUGIN_HANDLE)->xchat_list_int)
#define xchat_list_time ((XCHAT_PLUGIN_HANDLE)->xchat_list_time)
#define xchat_list_next ((XCHAT_PLUGIN_HANDLE)->xchat_list_next)
#define xchat_plugingui_add ((XCHAT_PLUGIN_HANDLE)->xchat_plugingui_add)
#define xchat_plugingui_remove ((XCHAT_PLUGIN_HANDLE)->xchat_plugingui_remove)
#define xchat_emit_print ((XCHAT_PLUGIN_HANDLE)->xchat_emit_print)
#define xchat_gettext ((XCHAT_PLUGIN_HANDLE)->xchat_gettext)
#define xchat_send_modes ((XCHAT_PLUGIN_HANDLE)->xchat_send_modes)
#define xchat_strip ((XCHAT_PLUGIN_HANDLE)->xchat_strip)
#define xchat_free ((XCHAT_PLUGIN_HANDLE)->xchat_free)
#endif

#ifdef __cplusplus
}
#endif

/******************************************************************
* Globals
******************************************************************/
extern xchat_plugin *ph;

/******************************************************************
* Prototypes
******************************************************************/
void wmpaCommands(void);
int wmpaAuto(char *word[], char *word_eol[], void *user_data);
int wmpaCurr(char *word[], char *word_eol[], void *user_data);
int wmpaFind(char *word[], char *word_eol[], void *user_data);
int wmpaList(char *word[], char *word_eol[], void *user_data);
int wmpaNext(char *word[], char *word_eol[], void *user_data);
int wmpaPlay(char *word[], char *word_eol[], void *user_data);
int wmpaPause(char *word[], char *word_eol[], void *user_data);
int wmpaPrev(char *word[], char *word_eol[], void *user_data);
int wmpaSong(char *word[], char *word_eol[], void *user_data);
int wmpaStop(char *word[], char *word_eol[], void *user_data);
int wmpaVolume(char *word[], char *word_eol[], void *user_data);
int wmpaHelp(char *word[], char *word_eol[], void *user_data);
BOOL wmpaRestoreSettings(void);
BOOL wmpaSaveSettings(void);
CString wmpaGetSongTitle(void);
BOOL SaveSetting(LPCTSTR name, DWORD type, CONST BYTE *value, DWORD size);
BOOL GetSetting(LPCTSTR name, DWORD *type, LPBYTE value, DWORD *size);

#endif /* XCHAT_PLUGIN_H */
uot;, enchant_dict_store_replacement) MODULE_SYMBOL("enchant_dict_suggest", enchant_dict_suggest) #undef MODULE_SYMBOL } static void sexy_spell_entry_class_init(SexySpellEntryClass *klass) { GObjectClass *gobject_class; GtkObjectClass *object_class; GtkWidgetClass *widget_class; GtkEntryClass *entry_class; initialize_enchant(); parent_class = g_type_class_peek_parent(klass); gobject_class = G_OBJECT_CLASS(klass); object_class = GTK_OBJECT_CLASS(klass); widget_class = GTK_WIDGET_CLASS(klass); entry_class = GTK_ENTRY_CLASS(klass); if (have_enchant) klass->word_check = default_word_check; gobject_class->finalize = sexy_spell_entry_finalize; object_class->destroy = sexy_spell_entry_destroy; widget_class->expose_event = sexy_spell_entry_expose; widget_class->button_press_event = sexy_spell_entry_button_press; /** * SexySpellEntry::word-check: * @entry: The entry on which the signal is emitted. * @word: The word to check. * * The ::word-check signal is emitted whenever the entry has to check * a word. This allows the application to mark words as correct even * if none of the active dictionaries contain it, such as nicknames in * a chat client. * * Returns: %FALSE to indicate that the word should be marked as * correct. */ signals[WORD_CHECK] = g_signal_new("word_check", G_TYPE_FROM_CLASS(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(SexySpellEntryClass, word_check), (GSignalAccumulator) spell_accumulator, NULL, sexy_marshal_BOOLEAN__STRING, G_TYPE_BOOLEAN, 1, G_TYPE_STRING); } static void sexy_spell_entry_editable_init (GtkEditableClass *iface) { } static gint gtk_entry_find_position (GtkEntry *entry, gint x) { PangoLayout *layout; PangoLayoutLine *line; const gchar *text; gint cursor_index; gint index; gint pos; gboolean trailing; x = x + entry->scroll_offset; layout = gtk_entry_get_layout(entry); text = pango_layout_get_text(layout); cursor_index = g_utf8_offset_to_pointer(text, entry->current_pos) - text; line = pango_layout_get_lines(layout)->data; pango_layout_line_x_to_index(line, x * PANGO_SCALE, &index, &trailing); if (index >= cursor_index && entry->preedit_length) { if (index >= cursor_index + entry->preedit_length) { index -= entry->preedit_length; } else { index = cursor_index; trailing = FALSE; } } pos = g_utf8_pointer_to_offset (text, text + index); pos += trailing; return pos; } static void insert_underline(SexySpellEntry *entry, guint start, guint end) { int fh, l; int red, green, blue; struct stat st; char *cfg; PangoAttribute *ucolor; PangoAttribute *unline; fh = xchat_open_file ("colors.conf", O_RDONLY, 0, 0); if (fh != -1) { fstat (fh, &st); cfg = malloc (st.st_size + 1); if (cfg) { cfg[0] = '\0'; l = read (fh, cfg, st.st_size); if (l >= 0) { cfg[l] = '\0'; } cfg_get_color (cfg, "color_265", &red, &green, &blue); free (cfg); } close (fh); } else { red = 65535; green = blue = 0; } ucolor = pango_attr_underline_color_new (red, green, blue); unline = pango_attr_underline_new (PANGO_UNDERLINE_ERROR); ucolor->start_index = start; unline->start_index = start; ucolor->end_index = end; unline->end_index = end; pango_attr_list_insert (entry->priv->attr_list, ucolor); pango_attr_list_insert (entry->priv->attr_list, unline); } static void get_word_extents_from_position(SexySpellEntry *entry, gint *start, gint *end, guint position) { const gchar *text; gint i, bytes_pos; *start = -1; *end = -1; if (entry->priv->words == NULL) return; text = gtk_entry_get_text(GTK_ENTRY(entry)); bytes_pos = (gint) (g_utf8_offset_to_pointer(text, position) - text); for (i = 0; entry->priv->words[i]; i++) { if (bytes_pos >= entry->priv->word_starts[i] && bytes_pos <= entry->priv->word_ends[i]) { *start = entry->priv->word_starts[i]; *end = entry->priv->word_ends[i]; return; } } } static void add_to_dictionary(GtkWidget *menuitem, SexySpellEntry *entry) { char *word; gint start, end; struct EnchantDict *dict; if (!have_enchant) return; get_word_extents_from_position(entry, &start, &end, entry->priv->mark_character); word = gtk_editable_get_chars(GTK_EDITABLE(entry), start, end); dict = (struct EnchantDict *) g_object_get_data(G_OBJECT(menuitem), "enchant-dict"); if (dict) enchant_dict_add_to_personal(dict, word, -1); g_free(word); if (entry->priv->words) { g_strfreev(entry->priv->words); g_free(entry->priv->word_starts); g_free(entry->priv->word_ends); } entry_strsplit_utf8(GTK_ENTRY(entry), &entry->priv->words, &entry->priv->word_starts, &entry->priv->word_ends); sexy_spell_entry_recheck_all(entry); } static void ignore_all(GtkWidget *menuitem, SexySpellEntry *entry) { char *word; gint start, end; GSList *li; if (!have_enchant) return; get_word_extents_from_position(entry, &start, &end, entry->priv->mark_character); word = gtk_editable_get_chars(GTK_EDITABLE(entry), start, end); for (li = entry->priv->dict_list; li; li = g_slist_next (li)) { struct EnchantDict *dict = (struct EnchantDict *) li->data; enchant_dict_add_to_session(dict, word, -1); } g_free(word); if (entry->priv->words) { g_strfreev(entry->priv->words); g_free(entry->priv->word_starts); g_free(entry->priv->word_ends); } entry_strsplit_utf8(GTK_ENTRY(entry), &entry->priv->words, &entry->priv->word_starts, &entry->priv->word_ends); sexy_spell_entry_recheck_all(entry); } static void replace_word(GtkWidget *menuitem, SexySpellEntry *entry) { char *oldword; const char *newword; gint start, end; gint cursor; struct EnchantDict *dict; if (!have_enchant) return; get_word_extents_from_position(entry, &start, &end, entry->priv->mark_character); oldword = gtk_editable_get_chars(GTK_EDITABLE(entry), start, end); newword = gtk_label_get_text(GTK_LABEL(GTK_BIN(menuitem)->child)); cursor = gtk_editable_get_position(GTK_EDITABLE(entry)); /* is the cursor at the end? If so, restore it there */ if (g_utf8_strlen(gtk_entry_get_text(GTK_ENTRY(entry)), -1) == cursor) cursor = -1; else if(cursor > start && cursor <= end) cursor = start; gtk_editable_delete_text(GTK_EDITABLE(entry), start, end); gtk_editable_set_position(GTK_EDITABLE(entry), start); gtk_editable_insert_text(GTK_EDITABLE(entry), newword, strlen(newword), &start); gtk_editable_set_position(GTK_EDITABLE(entry), cursor); dict = (struct EnchantDict *) g_object_get_data(G_OBJECT(menuitem), "enchant-dict"); if (dict) enchant_dict_store_replacement(dict, oldword, -1, newword, -1); g_free(oldword); } static void build_suggestion_menu(SexySpellEntry *entry, GtkWidget *menu, struct EnchantDict *dict, const gchar *word) { GtkWidget *mi; gchar **suggestions; size_t n_suggestions, i; if (!have_enchant) return; suggestions = enchant_dict_suggest(dict, word, -1, &n_suggestions); if (suggestions == NULL || n_suggestions == 0) { /* no suggestions. put something in the menu anyway... */ GtkWidget *label = gtk_label_new(""); gtk_label_set_markup(GTK_LABEL(label), _("<i>(no suggestions)</i>")); mi = gtk_separator_menu_item_new(); gtk_container_add(GTK_CONTAINER(mi), label); gtk_widget_show_all(mi); gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); } else { /* build a set of menus with suggestions */ for (i = 0; i < n_suggestions; i++) { if ((i != 0) && (i % 10 == 0)) { mi = gtk_separator_menu_item_new(); gtk_widget_show(mi); gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi); mi = gtk_menu_item_new_with_label(_("More...")); gtk_widget_show(mi); gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi); menu = gtk_menu_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi), menu); } mi = gtk_menu_item_new_with_label(suggestions[i]); g_object_set_data(G_OBJECT(mi), "enchant-dict", dict); g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(replace_word), entry); gtk_widget_show(mi); gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi); } } enchant_dict_free_suggestions(dict, suggestions); } static GtkWidget * build_spelling_menu(SexySpellEntry *entry, const gchar *word) { struct EnchantDict *dict; GtkWidget *topmenu, *mi; gchar *label; if (!have_enchant) return NULL; topmenu = gtk_menu_new(); if (entry->priv->dict_list == NULL) return topmenu; /* Suggestions */ if (g_slist_length(entry->priv->dict_list) == 1) { dict = (struct EnchantDict *) entry->priv->dict_list->data; build_suggestion_menu(entry, topmenu, dict, word); } else { GSList *li; GtkWidget *menu; gchar *lang, *lang_name; for (li = entry->priv->dict_list; li; li = g_slist_next (li)) { dict = (struct EnchantDict *) li->data; lang = get_lang_from_dict(dict); lang_name = gtkspell_iso_codes_lookup_name_for_code(lang); if (lang_name) { mi = gtk_menu_item_new_with_label(lang_name); g_free(lang_name); } else { mi = gtk_menu_item_new_with_label(lang); } g_free(lang); gtk_widget_show(mi); gtk_menu_shell_append(GTK_MENU_SHELL(topmenu), mi); menu = gtk_menu_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi), menu); build_suggestion_menu(entry, menu, dict, word); } } /* Separator */ mi = gtk_separator_menu_item_new (); gtk_widget_show(mi); gtk_menu_shell_append(GTK_MENU_SHELL(topmenu), mi); /* + Add to Dictionary */ label = g_strdup_printf(_("Add \"%s\" to Dictionary"), word); mi = gtk_image_menu_item_new_with_label(label); g_free(label); gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU)); if (g_slist_length(entry->priv->dict_list) == 1) { dict = (struct EnchantDict *) entry->priv->dict_list->data; g_object_set_data(G_OBJECT(mi), "enchant-dict", dict); g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(add_to_dictionary), entry); } else { GSList *li; GtkWidget *menu, *submi; gchar *lang, *lang_name; menu = gtk_menu_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi), menu); for (li = entry->priv->dict_list; li; li = g_slist_next(li)) { dict = (struct EnchantDict *)li->data; lang = get_lang_from_dict(dict); lang_name = gtkspell_iso_codes_lookup_name_for_code(lang); if (lang_name) { submi = gtk_menu_item_new_with_label(lang_name); g_free(lang_name); } else { submi = gtk_menu_item_new_with_label(lang); } g_free(lang); g_object_set_data(G_OBJECT(submi), "enchant-dict", dict); g_signal_connect(G_OBJECT(submi), "activate", G_CALLBACK(add_to_dictionary), entry); gtk_widget_show(submi); gtk_menu_shell_append(GTK_MENU_SHELL(menu), submi); } } gtk_widget_show_all(mi); gtk_menu_shell_append(GTK_MENU_SHELL(topmenu), mi); /* - Ignore All */ mi = gtk_image_menu_item_new_with_label(_("Ignore All")); gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), gtk_image_new_from_stock(GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU)); g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(ignore_all), entry); gtk_widget_show_all(mi); gtk_menu_shell_append(GTK_MENU_SHELL(topmenu), mi); return topmenu; } static void sexy_spell_entry_populate_popup(SexySpellEntry *entry, GtkMenu *menu, gpointer data) { GtkWidget *icon, *mi; gint start, end; gchar *word; if ((have_enchant == FALSE) || (entry->priv->checked == FALSE)) return; if (g_slist_length(entry->priv->dict_list) == 0) return; get_word_extents_from_position(entry, &start, &end, entry->priv->mark_character); if (start == end) return; if (!word_misspelled(entry, start, end)) return; /* separator */ mi = gtk_separator_menu_item_new(); gtk_widget_show(mi); gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); /* Above the separator, show the suggestions menu */ icon = gtk_image_new_from_stock(GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU); mi = gtk_image_menu_item_new_with_label(_("Spelling Suggestions")); gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), icon); word = gtk_editable_get_chars(GTK_EDITABLE(entry), start, end); g_assert(word != NULL); gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi), build_spelling_menu(entry, word)); g_free(word); gtk_widget_show_all(mi); gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); } static void sexy_spell_entry_init(SexySpellEntry *entry) { entry->priv = g_new0(SexySpellEntryPriv, 1); entry->priv->dict_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); if (have_enchant) sexy_spell_entry_activate_default_languages(entry); entry->priv->attr_list = pango_attr_list_new(); entry->priv->checked = TRUE; g_signal_connect(G_OBJECT(entry), "popup-menu", G_CALLBACK(sexy_spell_entry_popup_menu), entry); g_signal_connect(G_OBJECT(entry), "populate-popup", G_CALLBACK(sexy_spell_entry_populate_popup), NULL); g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(sexy_spell_entry_changed), NULL); } static void sexy_spell_entry_finalize(GObject *obj) { SexySpellEntry *entry; g_return_if_fail(obj != NULL); g_return_if_fail(SEXY_IS_SPELL_ENTRY(obj)); entry = SEXY_SPELL_ENTRY(obj); if (entry->priv->attr_list) pango_attr_list_unref(entry->priv->attr_list); if (entry->priv->dict_hash) g_hash_table_destroy(entry->priv->dict_hash); if (entry->priv->words) g_strfreev(entry->priv->words); if (entry->priv->word_starts) g_free(entry->priv->word_starts); if (entry->priv->word_ends) g_free(entry->priv->word_ends); if (have_enchant) { if (entry->priv->broker) { GSList *li; for (li = entry->priv->dict_list; li; li = g_slist_next(li)) { struct EnchantDict *dict = (struct EnchantDict*) li->data; enchant_broker_free_dict (entry->priv->broker, dict); } g_slist_free (entry->priv->dict_list); enchant_broker_free(entry->priv->broker); } } g_free(entry->priv); if (G_OBJECT_CLASS(parent_class)->finalize) G_OBJECT_CLASS(parent_class)->finalize(obj); } static void sexy_spell_entry_destroy(GtkObject *obj) { SexySpellEntry *entry; entry = SEXY_SPELL_ENTRY(obj); if (GTK_OBJECT_CLASS(parent_class)->destroy) GTK_OBJECT_CLASS(parent_class)->destroy(obj); } /** * sexy_spell_entry_new * * Creates a new SexySpellEntry widget. * * Returns: a new #SexySpellEntry. */ GtkWidget * sexy_spell_entry_new(void) { return GTK_WIDGET(g_object_new(SEXY_TYPE_SPELL_ENTRY, NULL)); } GQuark sexy_spell_error_quark(void) { static GQuark q = 0; if (q == 0) q = g_quark_from_static_string("sexy-spell-error-quark"); return q; } static gboolean default_word_check(SexySpellEntry *entry, const gchar *word) { gboolean result = TRUE; GSList *li; if (!have_enchant) return result; if (g_unichar_isalpha(*word) == FALSE) { /* We only want to check words */ return FALSE; } for (li = entry->priv->dict_list; li; li = g_slist_next (li)) { struct EnchantDict *dict = (struct EnchantDict *) li->data; if (enchant_dict_check(dict, word, strlen(word)) == 0) { result = FALSE; break; } } return result; } static gboolean word_misspelled(SexySpellEntry *entry, int start, int end) { const gchar *text; gchar *word; gboolean ret; if (start == end) return FALSE; text = gtk_entry_get_text(GTK_ENTRY(entry)); word = g_new0(gchar, end - start + 2); g_strlcpy(word, text + start, end - start + 1); g_signal_emit(entry, signals[WORD_CHECK], 0, word, &ret); g_free(word); return ret; } static void check_word(SexySpellEntry *entry, int start, int end) { PangoAttrIterator *it; /* Check to see if we've got any attributes at this position. * If so, free them, since we'll readd it if the word is misspelled */ it = pango_attr_list_get_iterator(entry->priv->attr_list); if (it == NULL) return; do { gint s, e; pango_attr_iterator_range(it, &s, &e); if (s == start) { GSList *attrs = pango_attr_iterator_get_attrs(it); g_slist_foreach(attrs, (GFunc) pango_attribute_destroy, NULL); g_slist_free(attrs); } } while (pango_attr_iterator_next(it)); pango_attr_iterator_destroy(it); if (word_misspelled(entry, start, end)) insert_underline(entry, start, end); } static void sexy_spell_entry_recheck_all(SexySpellEntry *entry) { GdkRectangle rect; GtkWidget *widget = GTK_WIDGET(entry); PangoLayout *layout; int length, i; if ((have_enchant == FALSE) || (entry->priv->checked == FALSE)) return; if (g_slist_length(entry->priv->dict_list) == 0) return; /* Remove all existing pango attributes. These will get readded as we check */ pango_attr_list_unref(entry->priv->attr_list); entry->priv->attr_list = pango_attr_list_new(); /* Loop through words */ for (i = 0; entry->priv->words[i]; i++) { length = strlen(entry->priv->words[i]); if (length == 0) continue; check_word(entry, entry->priv->word_starts[i], entry->priv->word_ends[i]); } layout = gtk_entry_get_layout(GTK_ENTRY(entry)); pango_layout_set_attributes(layout, entry->priv->attr_list); if (GTK_WIDGET_REALIZED(GTK_WIDGET(entry))) { rect.x = 0; rect.y = 0; rect.width = widget->allocation.width; rect.height = widget->allocation.height; gdk_window_invalidate_rect(widget->window, &rect, TRUE); } } static gint sexy_spell_entry_expose(GtkWidget *widget, GdkEventExpose *event) { SexySpellEntry *entry = SEXY_SPELL_ENTRY(widget); GtkEntry *gtk_entry = GTK_ENTRY(widget); PangoLayout *layout; if (entry->priv->checked) { layout = gtk_entry_get_layout(gtk_entry); pango_layout_set_attributes(layout, entry->priv->attr_list); } return GTK_WIDGET_CLASS(parent_class)->expose_event (widget, event); } static gint sexy_spell_entry_button_press(GtkWidget *widget, GdkEventButton *event) { SexySpellEntry *entry = SEXY_SPELL_ENTRY(widget); GtkEntry *gtk_entry = GTK_ENTRY(widget); gint pos; pos = gtk_entry_find_position(gtk_entry, event->x); entry->priv->mark_character = pos; return GTK_WIDGET_CLASS(parent_class)->button_press_event (widget, event); } static gboolean sexy_spell_entry_popup_menu(GtkWidget *widget, SexySpellEntry *entry) { /* Menu popped up from a keybinding (menu key or <shift>+F10). Use * the cursor position as the mark position */ entry->priv->mark_character = gtk_editable_get_position (GTK_EDITABLE (entry)); return FALSE; } static void entry_strsplit_utf8(GtkEntry *entry, gchar ***set, gint **starts, gint **ends) { PangoLayout *layout; PangoLogAttr *log_attrs; const gchar *text; gint n_attrs, n_strings, i, j; layout = gtk_entry_get_layout(GTK_ENTRY(entry)); text = gtk_entry_get_text(GTK_ENTRY(entry)); pango_layout_get_log_attrs(layout, &log_attrs, &n_attrs); /* Find how many words we have */ n_strings = 0; for (i = 0; i < n_attrs; i++) if (log_attrs[i].is_word_start) n_strings++; *set = g_new0(gchar *, n_strings + 1); *starts = g_new0(gint, n_strings); *ends = g_new0(gint, n_strings); /* Copy out strings */ for (i = 0, j = 0; i < n_attrs; i++) { if (log_attrs[i].is_word_start) { gint cend, bytes; gchar *start; /* Find the end of this string */ cend = i; while (!(log_attrs[cend].is_word_end)) cend++; /* Copy sub-string */ start = g_utf8_offset_to_pointer(text, i); bytes = (gint) (g_utf8_offset_to_pointer(text, cend) - start); (*set)[j] = g_new0(gchar, bytes + 1); (*starts)[j] = (gint) (start - text); (*ends)[j] = (gint) (start - text + bytes); g_utf8_strncpy((*set)[j], start, cend - i); /* Move on to the next word */ j++; } } g_free (log_attrs); } static void sexy_spell_entry_changed(GtkEditable *editable, gpointer data) { SexySpellEntry *entry = SEXY_SPELL_ENTRY(editable); if (entry->priv->checked == FALSE) return; if (g_slist_length(entry->priv->dict_list) == 0) return; if (entry->priv->words) { g_strfreev(entry->priv->words); g_free(entry->priv->word_starts); g_free(entry->priv->word_ends); } entry_strsplit_utf8(GTK_ENTRY(entry), &entry->priv->words, &entry->priv->word_starts, &entry->priv->word_ends); sexy_spell_entry_recheck_all(entry); } static gboolean enchant_has_lang(const gchar *lang, GSList *langs) { GSList *i; for (i = langs; i; i = g_slist_next(i)) { if (strcmp(lang, i->data) == 0) { return TRUE; } } return FALSE; } /** * sexy_spell_entry_activate_default_languages: * @entry: A #SexySpellEntry. * * Activate spell checking for languages specified in the $LANG * or $LANGUAGE environment variables. These languages are * activated by default, so this function need only be called * if they were previously deactivated. */ void sexy_spell_entry_activate_default_languages(SexySpellEntry *entry) { #if GLIB_CHECK_VERSION (2, 6, 0) /*const gchar* const *langs; int i; gchar *lastprefix = NULL;*/ GSList *enchant_langs, *i; if (!have_enchant) return; if (!entry->priv->broker) entry->priv->broker = enchant_broker_init(); /*langs = g_get_language_names (); if (langs == NULL) return;*/ enchant_langs = sexy_spell_entry_get_languages(entry); /*for (i = 0; langs[i]; i++) { if ((g_ascii_strncasecmp(langs[i], "C", 1) != 0) && (strlen(langs[i]) >= 2) && enchant_has_lang(langs[i], enchant_langs)) { if ((lastprefix == NULL) || (g_str_has_prefix(langs[i], lastprefix) == FALSE)) sexy_spell_entry_activate_language_internal(entry, langs[i], NULL); if (lastprefix != NULL) g_free(lastprefix); lastprefix = g_strndup(langs[i], 2); } } if (lastprefix != NULL) g_free(lastprefix);*/ for (i = enchant_langs; i; i = g_slist_next (i)) { if (strstr (prefs.spell_langs, i->data) != NULL) { sexy_spell_entry_activate_language_internal (entry, i->data, NULL); } } g_slist_foreach(enchant_langs, (GFunc) g_free, NULL); g_slist_free(enchant_langs); g_slist_free (i); /* If we don't have any languages activated, use "en" */ if (entry->priv->dict_list == NULL) sexy_spell_entry_activate_language_internal(entry, "en", NULL); #else gchar *lang; if (!have_enchant) return; lang = (gchar *) g_getenv("LANG"); if (lang != NULL) { if (g_ascii_strncasecmp(lang, "C", 1) == 0) lang = NULL; else if (lang[0] == '\0') lang = NULL; } if (lang == NULL) lang = "en"; sexy_spell_entry_activate_language_internal(entry, lang, NULL); #endif } static void get_lang_from_dict_cb(const char * const lang_tag, const char * const provider_name, const char * const provider_desc, const char * const provider_file, void * user_data) { gchar **lang = (gchar **)user_data; *lang = g_strdup(lang_tag); } static gchar * get_lang_from_dict(struct EnchantDict *dict) { gchar *lang; if (!have_enchant) return NULL; enchant_dict_describe(dict, get_lang_from_dict_cb, &lang); return lang; } static gboolean sexy_spell_entry_activate_language_internal(SexySpellEntry *entry, const gchar *lang, GError **error) { struct EnchantDict *dict; if (!have_enchant) return FALSE; if (!entry->priv->broker) entry->priv->broker = enchant_broker_init(); if (g_hash_table_lookup(entry->priv->dict_hash, lang)) return TRUE; dict = enchant_broker_request_dict(entry->priv->broker, lang); if (!dict) { g_set_error(error, SEXY_SPELL_ERROR, SEXY_SPELL_ERROR_BACKEND, _("enchant error for language: %s"), lang); return FALSE; } entry->priv->dict_list = g_slist_append(entry->priv->dict_list, (gpointer) dict); g_hash_table_insert(entry->priv->dict_hash, get_lang_from_dict(dict), (gpointer) dict); return TRUE; } static void dict_describe_cb(const char * const lang_tag, const char * const provider_name, const char * const provider_desc, const char * const provider_file, void * user_data) { GSList **langs = (GSList **)user_data; *langs = g_slist_append(*langs, (gpointer)g_strdup(lang_tag)); } /** * sexy_spell_entry_get_languages: * @entry: A #SexySpellEntry. * * Retrieve a list of language codes for which dictionaries are available. * * Returns: a new #GList object, or %NULL on error. */ GSList * sexy_spell_entry_get_languages(const SexySpellEntry *entry) { GSList *langs = NULL; g_return_val_if_fail(entry != NULL, NULL); g_return_val_if_fail(SEXY_IS_SPELL_ENTRY(entry), NULL); if (enchant_broker_list_dicts == NULL) return NULL; if (!entry->priv->broker) return NULL; enchant_broker_list_dicts(entry->priv->broker, dict_describe_cb, &langs); return langs; } /** * sexy_spell_entry_get_language_name: * @entry: A #SexySpellEntry. * @lang: The language code to lookup a friendly name for. * * Get a friendly name for a given locale. * * Returns: The name of the locale. Should be freed with g_free() */ gchar * sexy_spell_entry_get_language_name(const SexySpellEntry *entry, const gchar *lang) { if (have_enchant) return gtkspell_iso_codes_lookup_name_for_code(lang); return NULL; } /** * sexy_spell_entry_language_is_active: * @entry: A #SexySpellEntry. * @lang: The language to use, in a form enchant understands. * * Determine if a given language is currently active. * * Returns: TRUE if the language is active. */ gboolean sexy_spell_entry_language_is_active(const SexySpellEntry *entry, const gchar *lang) { return (g_hash_table_lookup(entry->priv->dict_hash, lang) != NULL); } /** * sexy_spell_entry_activate_language: * @entry: A #SexySpellEntry * @lang: The language to use in a form Enchant understands. Typically either * a two letter language code or a locale code in the form xx_XX. * @error: Return location for error. * * Activate spell checking for the language specifed. * * Returns: FALSE if there was an error. */ gboolean sexy_spell_entry_activate_language(SexySpellEntry *entry, const gchar *lang, GError **error) { gboolean ret; g_return_val_if_fail(entry != NULL, FALSE); g_return_val_if_fail(SEXY_IS_SPELL_ENTRY(entry), FALSE); g_return_val_if_fail(lang != NULL && lang != '\0', FALSE); if (!have_enchant) return FALSE; if (error) g_return_val_if_fail(*error == NULL, FALSE); ret = sexy_spell_entry_activate_language_internal(entry, lang, error); if (ret) { if (entry->priv->words) { g_strfreev(entry->priv->words); g_free(entry->priv->word_starts); g_free(entry->priv->word_ends); } entry_strsplit_utf8(GTK_ENTRY(entry), &entry->priv->words, &entry->priv->word_starts, &entry->priv->word_ends); sexy_spell_entry_recheck_all(entry); } return ret; } /** * sexy_spell_entry_deactivate_language: * @entry: A #SexySpellEntry. * @lang: The language in a form Enchant understands. Typically either * a two letter language code or a locale code in the form xx_XX. * * Deactivate spell checking for the language specifed. */ void sexy_spell_entry_deactivate_language(SexySpellEntry *entry, const gchar *lang) { g_return_if_fail(entry != NULL); g_return_if_fail(SEXY_IS_SPELL_ENTRY(entry)); if (!have_enchant) return; if (!entry->priv->dict_list) return; if (lang) { struct EnchantDict *dict; dict = g_hash_table_lookup(entry->priv->dict_hash, lang); if (!dict) return; enchant_broker_free_dict(entry->priv->broker, dict); entry->priv->dict_list = g_slist_remove(entry->priv->dict_list, dict); g_hash_table_remove (entry->priv->dict_hash, lang); } else { /* deactivate all */ GSList *li; struct EnchantDict *dict; for (li = entry->priv->dict_list; li; li = g_slist_next(li)) { dict = (struct EnchantDict *)li->data; enchant_broker_free_dict(entry->priv->broker, dict); } g_slist_free (entry->priv->dict_list); g_hash_table_destroy (entry->priv->dict_hash); entry->priv->dict_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); entry->priv->dict_list = NULL; } if (entry->priv->words) { g_strfreev(entry->priv->words); g_free(entry->priv->word_starts); g_free(entry->priv->word_ends); } entry_strsplit_utf8(GTK_ENTRY(entry), &entry->priv->words, &entry->priv->word_starts, &entry->priv->word_ends); sexy_spell_entry_recheck_all(entry); } /** * sexy_spell_entry_set_active_languages: * @entry: A #SexySpellEntry * @langs: A list of language codes to activate, in a form Enchant understands. * Typically either a two letter language code or a locale code in the * form xx_XX. * @error: Return location for error. * * Activate spell checking for only the languages specified. * * Returns: FALSE if there was an error. */ gboolean sexy_spell_entry_set_active_languages(SexySpellEntry *entry, GSList *langs, GError **error) { GSList *li; g_return_val_if_fail(entry != NULL, FALSE); g_return_val_if_fail(SEXY_IS_SPELL_ENTRY(entry), FALSE); g_return_val_if_fail(langs != NULL, FALSE); if (!have_enchant) return FALSE; /* deactivate all languages first */ sexy_spell_entry_deactivate_language(entry, NULL); for (li = langs; li; li = g_slist_next(li)) { if (sexy_spell_entry_activate_language_internal(entry, (const gchar *) li->data, error) == FALSE) return FALSE; } if (entry->priv->words) { g_strfreev(entry->priv->words); g_free(entry->priv->word_starts); g_free(entry->priv->word_ends); } entry_strsplit_utf8(GTK_ENTRY(entry), &entry->priv->words, &entry->priv->word_starts, &entry->priv->word_ends); sexy_spell_entry_recheck_all(entry); return TRUE; } /** * sexy_spell_entry_get_active_languages: * @entry: A #SexySpellEntry * * Retrieve a list of the currently active languages. * * Returns: A GSList of char* values with language codes (en, fr, etc). Both * the data and the list must be freed by the user. */ GSList * sexy_spell_entry_get_active_languages(SexySpellEntry *entry) { GSList *ret = NULL, *li; struct EnchantDict *dict; gchar *lang; g_return_val_if_fail(entry != NULL, NULL); g_return_val_if_fail(SEXY_IS_SPELL_ENTRY(entry), NULL); if (!have_enchant) return NULL; for (li = entry->priv->dict_list; li; li = g_slist_next(li)) { dict = (struct EnchantDict *) li->data; lang = get_lang_from_dict(dict); ret = g_slist_append(ret, lang); } return ret; } /** * sexy_spell_entry_is_checked: * @entry: A #SexySpellEntry. * * Queries a #SexySpellEntry and returns whether the entry has spell-checking enabled. * * Returns: TRUE if the entry has spell-checking enabled. */ gboolean sexy_spell_entry_is_checked(SexySpellEntry *entry) { return entry->priv->checked; } /** * sexy_spell_entry_set_checked: * @entry: A #SexySpellEntry. * @checked: Whether to enable spell-checking * * Sets whether the entry has spell-checking enabled. */ void sexy_spell_entry_set_checked(SexySpellEntry *entry, gboolean checked) { GtkWidget *widget; if (entry->priv->checked == checked) return; entry->priv->checked = checked; widget = GTK_WIDGET(entry); if (checked == FALSE && GTK_WIDGET_REALIZED(widget)) { PangoLayout *layout; GdkRectangle rect; pango_attr_list_unref(entry->priv->attr_list); entry->priv->attr_list = pango_attr_list_new(); layout = gtk_entry_get_layout(GTK_ENTRY(entry)); pango_layout_set_attributes(layout, entry->priv->attr_list); rect.x = 0; rect.y = 0; rect.width = widget->allocation.width; rect.height = widget->allocation.height; gdk_window_invalidate_rect(widget->window, &rect, TRUE); } else { if (entry->priv->words) { g_strfreev(entry->priv->words); g_free(entry->priv->word_starts); g_free(entry->priv->word_ends); } entry_strsplit_utf8(GTK_ENTRY(entry), &entry->priv->words, &entry->priv->word_starts, &entry->priv->word_ends); sexy_spell_entry_recheck_all(entry); } }