/* charpick.c This is a gnome panel applet that allow users to select * accented (and other) characters to be pasted into other apps. */ #include #include #include #include #include "charpick.h" /* The comment for each char list has the html entity names of the chars */ /* All gunicar codes should end in -1 */ /* This is the default list used when starting charpick the first time */ /* aacute, agrave, eacute, iacute, oacute, frac12, copy*/ /* static const gchar *def_list = "áàéíñóœ©"; */ static gunichar def_code[] = {225, 224, 233, 237, 241, 243, 189, 169, 1579, 8364, -1}; /* aacute, agrave, acirc, atilde, auml. aring, aelig, ordf */ /* static const gchar *a_list = "áàâãäåæª"; */ static gunichar a_code[] = {225, 224, 226, 227, 228, 229, 230, 170, -1}; /* static const gchar *cap_a_list = "ÁÀÂÃÄÅƪ"; */ static gunichar cap_a_code[] = {192, 193, 194, 195, 196, 197, 198, 170, -1}; /* ccedil, cent, copy */ /* static const gchar *c_list = "çÇ¢©"; */ static gunichar c_code[] = {231, 199, 162, 169, -1}; /* eacute, egrave, ecirc, euml, aelig */ /* static const gchar *e_list = "éèêëæ"; */ static gunichar e_code[] = {232, 233, 234, 235, 230, -1}; /* static const gchar *cap_e_list = "ÉÈÊËÆ"; */ static gunichar cap_e_code[] = {200, 201, 202, 203, 198, -1}; /* iacute, igrave, icirc, iuml */ /* static const gchar *i_list = "íìîï"; */ static gunichar i_code[] = {236, 237, 238, 239, -1}; /* static const gchar *cap_i_list = "ÍÌÎÏ"; */ static gunichar cap_i_code[] = {204, 205, 206, 207, -1}; /* ntilde (this is the most important line in this program.) */ /* static const gchar *n_list = "ñ, Ñ"; */ static gunichar n_code[] = {241, 209, -1}; /* oacute, ograve, ocirc, otilde, ouml, oslash, ordm */ /* static const gchar *o_list = "óòôõöøº"; */ static gunichar o_code[] = {242, 243, 244, 245, 246, 248, 176, -1}; /* static const gchar *cap_o_list = "ÓÒÔÕÖغ"; */ static gunichar cap_o_code[] = {210, 211, 212, 213, 214, 216, 176, -1}; /* szlig, sect, dollar */ /* static const gchar *s_list = "ߧ$"; */ static gunichar s_code[] = {223, 167, 36, -1}; /* eth, thorn */ /* static const gchar *t_list = "ðÐþÞ"; */ static gunichar t_code[] = {240, 208, 254, 222, -1}; /* uacute, ugrave, ucirc, uuml */ /* static const gchar *u_list = "úùûü"; */ static gunichar u_code[] = {249, 250, 251, 252, -1}; /* static const gchar *cap_u_list = "ÚÙÛÜ"; */ static gunichar cap_u_code[] = {217, 218, 219, 220, -1}; /* yacute, yuml, yen Yes, there is no capital yuml in iso 8859-1.*/ /* static const gchar *y_list = "ýÝÿ¥"; */ static gunichar y_code[] = {253, 221, 255, 165, -1}; /* extra characters unrelated to the latin alphabet. All characters in ISO-8859-1 should now be accounted for.*/ /* not shy macr plusmn */ /* static const gchar *dash_list = "¬­¯±"; */ static gunichar dash_code[] = {172, 173, 175, 177, -1}; /* laquo raquo uml */ /* static const gchar *quote_list = "«»š·×"; */ static gunichar quote_code[] = {171, 187, 168, 183, 215, -1}; /* curren, pound, yen, cent, dollar */ /* static const gchar *currency_list = "€£¥¢$"; */ static gunichar currency_code[] = {164, 163, 165, 162, 36, 8364, -1}; /* sup1 frac12 */ /* static const gchar *one_list = "¹œŒ"; */ static gunichar one_code[] = {185, 178, 179, 188, 189, 190, -1}; /* µ ¶ ® ¿ | */ static gunichar misc_code[] = {181, 182, 174, 191, 124, -1}; static const gunichar *chartable[] = { def_code, a_code, cap_a_code, c_code, e_code, cap_e_code, i_code, cap_i_code, n_code, o_code, cap_o_code, s_code, t_code, u_code, cap_u_code, y_code, dash_code, quote_code, currency_code, one_code, misc_code }; /* sets the picked character as the selection when it gets a request */ static void charpick_selection_handler(GtkWidget *widget, GtkSelectionData *selection_data, guint info, guint time, gpointer data) { charpick_data *p_curr_data = data; gint num; gchar tmp[7]; num = g_unichar_to_utf8 (p_curr_data->selected_unichar, tmp); tmp[num] = '\0'; gtk_selection_data_set_text (selection_data, tmp, -1); return; } /* untoggles the active toggle_button when we lose the selection */ static gint selection_clear_cb (GtkWidget *widget, GdkEventSelection *event, gpointer data) { charpick_data *curr_data = data; gint last_index = curr_data->last_index; if (curr_data->last_toggle_button) gtk_toggle_button_set_state (curr_data->last_toggle_button, FALSE); curr_data->last_toggle_button = NULL; return TRUE; } static gchar * get_utf_string (gunichar *codes) { gchar *string = NULL, tmp[7]; gint i = 0; while (codes[i] != -1) { gint num; num = g_unichar_to_utf8 (codes[i], tmp); tmp[num] = 0; if (string) string = g_strconcat (string, tmp, NULL); else string = g_strdup (tmp); i++; } return string; } static gint toggle_button_toggled_cb(GtkToggleButton *button, gpointer data) { charpick_data *curr_data = data; GtkClipboard *clipboard; gint button_index; gint last_index = curr_data->last_index; gboolean toggled; button_index = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), "index")); toggled = gtk_toggle_button_get_active (button); if (toggled) { gunichar unichar; if (curr_data->last_toggle_button && (button != curr_data->last_toggle_button)) gtk_toggle_button_set_active (curr_data->last_toggle_button, FALSE); curr_data->last_toggle_button = button; gtk_widget_grab_focus(curr_data->applet); unichar = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), "unichar")); curr_data->selected_unichar = unichar; /* set this? widget as the selection owner */ gtk_selection_owner_set (curr_data->applet, GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME); gtk_selection_owner_set (curr_data->applet, GDK_SELECTION_CLIPBOARD, GDK_CURRENT_TIME); curr_data->last_index = button_index; } return TRUE; } /* This is a hack around the fact that gtk+ doesn't * propogate button presses on button2/3. */ static gboolean button_press_hack (GtkWidget *widget, GdkEventButton *event, GtkWidget *applet) { if (event->button == 3 || event->button == 2) { gtk_propagate_event (applet, (GdkEvent *) event); return TRUE; } return FALSE; } static gint key_press_event(GtkWidget *widget, GdkEventKey *event, gpointer data) { charpick_data *p_curr_data = data; gint *code = NULL; gchar inputchar = event->keyval; switch (inputchar) { case 'a' : code = a_code; break; case 'A' : code = cap_a_code; break; case 'c' : case 'C' : code = c_code; break; case 'e' : code = e_code; break; case 'E' : code = cap_e_code; break; case 'i' : code = i_code; break; case 'I' : code = cap_i_code; break; case 'n' : case 'N' : code = n_code; break; case 'o' : code = o_code; break; case 'O' : code = cap_o_code; break; case 's' : code = s_code; break; case 't' : case 'T' : code = t_code; break; case 'u' : code = u_code; break; case 'U' : code = cap_u_code; break; case 'y' : case 'Y' : code = y_code; break; case '-' : code = dash_code; break; case '\"' : code = quote_code; break; case '$' : code = currency_code; break; case '1' : case '2' : case '3' : code = one_code; break; case 'd' : code = NULL; break; default : return FALSE; } #if 0 if (code) p_curr_data->charlist = get_utf_string (code); else p_curr_data->charlist = "hello"; p_curr_data->last_index = NO_LAST_INDEX; p_curr_data->last_toggle_button = NULL; build_table(p_curr_data); #endif return FALSE; } static void menuitem_activated (GtkMenuItem *menuitem, charpick_data *curr_data) { gchar *string; PanelApplet *applet = PANEL_APPLET (curr_data->applet); string = g_object_get_data (G_OBJECT (menuitem), "string"); if (g_ascii_strcasecmp (curr_data->charlist, string) == 0) return; curr_data->charlist = string; build_table (curr_data); panel_applet_gconf_set_string (applet, "current_list", curr_data->charlist, NULL); } static void add_palette (GtkMenuItem *menuitem, charpick_data *curr_data) { PanelApplet *applet = PANEL_APPLET (curr_data->applet); GList *list = curr_data->chartable; gchar *new; new = run_edit_dialog (NULL, _("Add Palette")); if (!new) return; list = g_list_append (list, new); save_chartable (curr_data); populate_menu (curr_data); } void populate_menu (charpick_data *curr_data) { GList *list = curr_data->chartable; GSList *group = NULL; GtkMenu *menu; GtkWidget *menuitem; if (curr_data->menu) gtk_widget_destroy (curr_data->menu); curr_data->menu = gtk_menu_new (); menu = GTK_MENU (curr_data->menu); while (list) { gchar *string = list->data; menuitem = gtk_radio_menu_item_new_with_label (group, string); group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menuitem)); gtk_widget_show (menuitem); g_object_set_data (G_OBJECT (menuitem), "string", string); g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (menuitem_activated), curr_data); gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); if (g_ascii_strcasecmp (curr_data->charlist, string) == 0) gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem), TRUE); list = g_list_next (list); } menuitem = gtk_separator_menu_item_new (); gtk_widget_show (menuitem); gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); menuitem = gtk_menu_item_new_with_label (_("Add a palette...")); gtk_widget_show (menuitem); g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (add_palette), curr_data); gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); } static void get_menu_pos (GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data) { charpick_data *curr_data = data; GtkRequisition reqmenu, reqapplet; gint tempx, tempy, width, height; gint screen_width, screen_height; gtk_widget_size_request (GTK_WIDGET (menu), &reqmenu); gdk_window_get_origin (GTK_WIDGET (curr_data->applet)->window, &tempx, &tempy); gdk_window_get_geometry (GTK_WIDGET (curr_data->applet)->window, NULL, NULL, &width, &height, NULL); switch (panel_applet_get_orient (PANEL_APPLET (curr_data->applet))) { case PANEL_APPLET_ORIENT_DOWN: tempy += height; break; case PANEL_APPLET_ORIENT_UP: tempy -= reqmenu.height; break; case PANEL_APPLET_ORIENT_LEFT: tempx -= reqmenu.width; break; case PANEL_APPLET_ORIENT_RIGHT: tempx += width; break; } screen_width = gdk_screen_width (); screen_height = gdk_screen_height (); *x = CLAMP (tempx, 0, MAX (0, screen_width - reqmenu.width)); *y = CLAMP (tempy, 0, MAX (0, screen_height - reqmenu.height)); } static void chooser_button_clicked (GtkButton *button, charpick_data *curr_data) { if (GTK_WIDGET_VISIBLE (curr_data->menu)) gtk_menu_popdown (GTK_MENU (curr_data->menu)); else gtk_menu_popup (GTK_MENU (curr_data->menu), NULL, NULL, get_menu_pos, curr_data, 0, gtk_get_current_event_time()); } /* creates table of buttons, sets up their callbacks, and packs the table in the event box */ void build_table(charpick_data *p_curr_data) { GtkWidget *box, *button_box, **row_box; GtkWidget *button, *arrow; GtkTooltips *tooltips; gint size; gint i = 0, j, len = g_utf8_strlen (p_curr_data->charlist, -1); GtkWidget *toggle_button[len]; gchar *charlist; gint width, height; gint max_width=0, max_height=0; gint size_ratio; if (p_curr_data->box) gtk_widget_destroy(p_curr_data->box); if (p_curr_data->panel_vertical == TRUE) box = gtk_vbox_new (FALSE, 0); else box = gtk_hbox_new (FALSE, 0); gtk_widget_show (box); p_curr_data->box = box; tooltips = gtk_tooltips_new (); button = gtk_button_new (); gtk_tooltips_set_tip (tooltips, button, _("Available palettes"), NULL); switch (panel_applet_get_orient (PANEL_APPLET (p_curr_data->applet))) { case PANEL_APPLET_ORIENT_DOWN: arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT); break; case PANEL_APPLET_ORIENT_UP: arrow = gtk_arrow_new (GTK_ARROW_UP, GTK_SHADOW_OUT); break; case PANEL_APPLET_ORIENT_LEFT: arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_OUT); break; case PANEL_APPLET_ORIENT_RIGHT: arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_OUT); break; } gtk_container_add (GTK_CONTAINER (button), arrow); gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); gtk_box_pack_start (GTK_BOX (box), button, TRUE, TRUE, 0); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (chooser_button_clicked), p_curr_data); g_signal_connect (G_OBJECT (button), "button_press_event", G_CALLBACK (button_press_hack), p_curr_data->applet); charlist = g_strdup (p_curr_data->charlist); for (i = 0; i < len; i++) { gchar label[7]; gint num; GtkRequisition req; g_utf8_strncpy (label, charlist, 1); charlist = g_utf8_next_char (charlist); toggle_button[i] = gtk_toggle_button_new_with_label (label); gtk_widget_show (toggle_button[i]); gtk_button_set_relief(GTK_BUTTON(toggle_button[i]), GTK_RELIEF_NONE); gtk_tooltips_set_tip (tooltips, toggle_button[i], _("Insert special characters"), NULL); gtk_widget_size_request (toggle_button[i], &req); max_width = MAX (max_width, req.width); max_height = MAX (max_height, req.height-2); g_object_set_data (G_OBJECT (toggle_button[i]), "unichar", GINT_TO_POINTER(g_utf8_get_char (label))); gtk_signal_connect (GTK_OBJECT (toggle_button[i]), "toggled", (GtkSignalFunc) toggle_button_toggled_cb, p_curr_data); gtk_signal_connect (GTK_OBJECT (toggle_button[i]), "button_press_event", (GtkSignalFunc) button_press_hack, p_curr_data->applet); } switch (panel_applet_get_orient (PANEL_APPLET (p_curr_data->applet))) { case PANEL_APPLET_ORIENT_DOWN: case PANEL_APPLET_ORIENT_UP: size_ratio = p_curr_data->panel_size / max_height; button_box = gtk_vbox_new (TRUE, 0); break; case PANEL_APPLET_ORIENT_LEFT: case PANEL_APPLET_ORIENT_RIGHT: size_ratio = p_curr_data->panel_size / max_width; button_box = gtk_hbox_new (TRUE, 0); break; } gtk_box_pack_start (GTK_BOX (box), button_box, TRUE, TRUE, 0); size_ratio = MAX (size_ratio, 1); row_box = g_new0 (GtkWidget *, size_ratio); for (i=0; i < size_ratio; i++) { if (!p_curr_data->panel_vertical) row_box[i] = gtk_hbox_new (TRUE, 0); else row_box[i] = gtk_vbox_new (TRUE, 0); gtk_box_pack_start (GTK_BOX (button_box), row_box[i], TRUE, TRUE, 0); } for (i = 0; i applet), box); gtk_widget_show_all (p_curr_data->box); p_curr_data->last_index = NO_LAST_INDEX; p_curr_data->last_toggle_button = NULL; } static void applet_change_pixel_size(PanelApplet *applet, gint size, gpointer data) { charpick_data *curr_data = data; curr_data->panel_size = size; build_table (curr_data); return; } static void applet_change_orient(PanelApplet *applet, PanelAppletOrient o, gpointer data) { charpick_data *curr_data = data; if (o == PANEL_APPLET_ORIENT_UP || o == PANEL_APPLET_ORIENT_DOWN) curr_data->panel_vertical = FALSE; else curr_data->panel_vertical = TRUE; build_table (curr_data); return; } static void about (BonoboUIComponent *uic, charpick_data *curr_data, const char *verb) { static GtkWidget *about_box = NULL; GdkPixbuf *pixbuf; GError *error = NULL; gchar *file; const char *authors[] = { /* If your charset supports it, please use U00F1 to replace the "n" * in "Muniz". */ _("Alexandre Muniz "), _("Kevin Vandersloot"), NULL }; const gchar *documenters[] = { NULL }; const gchar *translator_credits = _("translator_credits"); if (about_box) { gtk_window_set_screen (GTK_WINDOW (about_box), gtk_widget_get_screen (curr_data->applet)); gtk_window_present (GTK_WINDOW (about_box)); return; } file = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_PIXMAP, "charpick.png", FALSE, NULL); pixbuf = gdk_pixbuf_new_from_file (file, &error); g_free (file); if (error) { g_warning (G_STRLOC ": cannot open %s: %s", file, error->message); g_error_free (error); } about_box = gnome_about_new (_("Character Palette"), VERSION, _("Copyright (C) 1998"), _("Gnome Panel applet for selecting strange " "characters that are not on my keyboard. " "Released under GNU General Public Licence."), authors, documenters, strcmp (translator_credits, "translator_credits") != 0 ? translator_credits : NULL, pixbuf); if (pixbuf) gdk_pixbuf_unref (pixbuf); gtk_window_set_screen (GTK_WINDOW (about_box), gtk_widget_get_screen (curr_data->applet)); gtk_window_set_wmclass (GTK_WINDOW (about_box), "character palette", "Character Palette"); gnome_window_icon_set_from_file (GTK_WINDOW (about_box), GNOME_ICONDIR"/charpick.png"); gtk_signal_connect(GTK_OBJECT(about_box), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &about_box); gtk_widget_show(about_box); return; } static void help_cb (BonoboUIComponent *uic, charpick_data *curr_data, const char *verb) { GError *error = NULL; egg_help_display_on_screen ( "char-palette", NULL, gtk_widget_get_screen (curr_data->applet), &error); if (error) { /* FIXME: the user needs to see this */ g_warning ("help error: %s\n", error->message); g_error_free (error); error = NULL; } } static void applet_destroy (GtkWidget *widget, gpointer data) { charpick_data *curr_data = data; GtkWidget *applet = curr_data->applet; g_return_if_fail (curr_data); if (curr_data->propwindow) gtk_widget_destroy (curr_data->propwindow); g_free (curr_data); } void save_chartable (charpick_data *curr_data) { PanelApplet *applet = PANEL_APPLET (curr_data->applet); GConfValue *value; GList *list = curr_data->chartable; GSList *slist = NULL; while (list) { gchar *charlist = list->data; GConfValue *v1; v1 = gconf_value_new_from_string (GCONF_VALUE_STRING, charlist, NULL); slist = g_slist_append (slist, v1); list = g_list_next (list); } value = gconf_value_new (GCONF_VALUE_LIST); gconf_value_set_list_type (value, GCONF_VALUE_STRING); gconf_value_set_list_nocopy (value, slist); panel_applet_gconf_set_value (applet, "chartable", value, NULL); gconf_value_free (value); } static void get_chartable (charpick_data *curr_data) { PanelApplet *applet = PANEL_APPLET (curr_data->applet); GConfValue *value; gint i, n; value = panel_applet_gconf_get_value (applet, "chartable", NULL); if (value) { GSList *slist = gconf_value_get_list (value); while (slist) { GConfValue *v1 = slist->data; gchar *charlist; charlist = g_strdup (gconf_value_get_string (v1)); curr_data->chartable = g_list_append (curr_data->chartable, charlist); slist = g_slist_next (slist); } gconf_value_free (value); } else { n = G_N_ELEMENTS (chartable); for (i=0; ichartable = g_list_append (curr_data->chartable, string); } save_chartable (curr_data); } } static const BonoboUIVerb charpick_applet_menu_verbs [] = { BONOBO_UI_UNSAFE_VERB ("Preferences", show_preferences_dialog), BONOBO_UI_UNSAFE_VERB ("Help", help_cb), BONOBO_UI_UNSAFE_VERB ("About", about), BONOBO_UI_VERB_END }; void set_atk_name_description (GtkWidget *widget, const gchar *name, const gchar *description) { AtkObject *aobj; aobj = gtk_widget_get_accessible (widget); /* return if gail is not loaded */ if (GTK_IS_ACCESSIBLE (aobj) == FALSE) return; atk_object_set_name (aobj, name); atk_object_set_description (aobj, description); } static void make_applet_accessible (GtkWidget *applet) { set_atk_name_description (applet, _("Character Palette"), NULL); } static gboolean charpicker_applet_fill (PanelApplet *applet) { PanelAppletOrient orientation; charpick_data *curr_data; GdkAtom utf8_atom; GList *list; gchar *string; gnome_window_icon_set_default_from_file (GNOME_ICONDIR"/charpick.png"); panel_applet_add_preferences (applet, "/schemas/apps/charpick/prefs", NULL); panel_applet_set_flags (applet, PANEL_APPLET_EXPAND_MINOR); curr_data = g_new0 (charpick_data, 1); curr_data->last_index = NO_LAST_INDEX; curr_data->applet = GTK_WIDGET (applet); get_chartable (curr_data); string = panel_applet_gconf_get_string (applet, "current_list", NULL); if (string) { list = curr_data->chartable; while (list) { if (g_ascii_strcasecmp (list->data, string) == 0) curr_data->charlist = list->data; list = g_list_next (list); } g_free (string); } else curr_data->charlist = curr_data->chartable->data; curr_data->panel_size = panel_applet_get_size (applet); orientation = panel_applet_get_orient (applet); curr_data->panel_vertical = (orientation == PANEL_APPLET_ORIENT_LEFT) || (orientation == PANEL_APPLET_ORIENT_RIGHT); build_table (curr_data); g_signal_connect (G_OBJECT (curr_data->applet), "key_press_event", G_CALLBACK (key_press_event), curr_data); utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE); gtk_selection_add_target (curr_data->applet, GDK_SELECTION_PRIMARY, utf8_atom, 0); gtk_selection_add_target (curr_data->applet, GDK_SELECTION_CLIPBOARD, utf8_atom, 0); gtk_signal_connect (GTK_OBJECT (curr_data->applet), "selection_get", GTK_SIGNAL_FUNC (charpick_selection_handler), curr_data); gtk_signal_connect (GTK_OBJECT (curr_data->applet), "selection_clear_event", GTK_SIGNAL_FUNC (selection_clear_cb), curr_data); make_applet_accessible (GTK_WIDGET (applet)); /* session save signal */ g_signal_connect (G_OBJECT (applet), "change_orient", G_CALLBACK (applet_change_orient), curr_data); g_signal_connect (G_OBJECT (applet), "change_size", G_CALLBACK (applet_change_pixel_size), curr_data); g_signal_connect (G_OBJECT (applet), "destroy", G_CALLBACK (applet_destroy), curr_data); gtk_widget_show_all (GTK_WIDGET (applet)); panel_applet_setup_menu_from_file (PANEL_APPLET (applet), NULL, "GNOME_CharpickerApplet.xml", NULL, charpick_applet_menu_verbs, curr_data); populate_menu (curr_data); return TRUE; } static gboolean charpicker_applet_factory (PanelApplet *applet, const gchar *iid, gpointer data) { gboolean retval = FALSE; if (!strcmp (iid, "OAFIID:GNOME_CharpickerApplet")) retval = charpicker_applet_fill (applet); return retval; } PANEL_APPLET_BONOBO_FACTORY ("OAFIID:GNOME_CharpickerApplet_Factory", PANEL_TYPE_APPLET, "char-palette", "0", charpicker_applet_factory, NULL)