diff --git a/CMakeLists.txt b/CMakeLists.txt
index 224a212c99..3214485bc8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -223,7 +223,6 @@ setup_build_flags(${ENABLE_MAINTAINER_MODE})
set(MATH_LDFLAGS -lm)
CHECK_INCLUDE_FILE(sys/wait.h HAVE_SYS_WAIT_H)
-CHECK_INCLUDE_FILE(X11/XF86keysym.h HAVE_XFREE)
CHECK_FUNCTION_EXISTS(nl_langinfo HAVE_NL_LANGINFO)
# ******************************
diff --git a/config.h.in b/config.h.in
index a0b4b6e6bb..7b77a9d91f 100644
--- a/config.h.in
+++ b/config.h.in
@@ -66,9 +66,6 @@
/* ISO codes prefix */
#define ISO_CODES_PREFIX "@ISO_CODES_PREFIX@"
-/* defined if you have X11/XF86keysym.h */
-#cmakedefine HAVE_XFREE 1
-
/* Define if TNEF attachments parser should be built */
#cmakedefine ENABLE_YTNEF 1
diff --git a/data/icons/CMakeLists.txt b/data/icons/CMakeLists.txt
index 104bbc8c00..8a3d70141b 100644
--- a/data/icons/CMakeLists.txt
+++ b/data/icons/CMakeLists.txt
@@ -58,6 +58,7 @@ set(private_icons
hicolor_apps_scalable_evolution-mail-symbolic.svg
hicolor_apps_scalable_evolution-memos-symbolic.svg
hicolor_apps_scalable_evolution-tasks-symbolic.svg
+ hicolor_apps_scalable_gcr-gnupg.svg
hicolor_apps_scalable_im-aim.svg
hicolor_apps_scalable_im-icq.svg
hicolor_apps_scalable_im-jabber.svg
diff --git a/data/icons/hicolor_apps_scalable_gcr-gnupg.svg b/data/icons/hicolor_apps_scalable_gcr-gnupg.svg
new file mode 100644
index 0000000000..36ffd688b3
--- /dev/null
+++ b/data/icons/hicolor_apps_scalable_gcr-gnupg.svg
@@ -0,0 +1,102 @@
+
+
+
+
diff --git a/data/ui/CMakeLists.txt b/data/ui/CMakeLists.txt
index 1c3d04a56a..f4222ca699 100644
--- a/data/ui/CMakeLists.txt
+++ b/data/ui/CMakeLists.txt
@@ -1,11 +1,14 @@
-install(FILES evolution-calendars.ui
- evolution-contacts.ui
- evolution-mail.ui
- evolution-mail-reader.ui
- evolution-mail-viewer.ui
- evolution-memos.ui
- evolution-shell.ui
- evolution-tasks.ui
+install(FILES e-comp-editor.eui
+ e-html-editor.eui
+ evolution-calendars.eui
+ evolution-composer.eui
+ evolution-contacts.eui
+ evolution-mail.eui
+ evolution-mail-reader.eui
+ evolution-mail-viewer.eui
+ evolution-memos.eui
+ evolution-shell.eui
+ evolution-tasks.eui
evolution-shortcuts.ui
DESTINATION ${uidir}
)
diff --git a/data/ui/e-comp-editor.eui b/data/ui/e-comp-editor.eui
new file mode 100644
index 0000000000..0f4d3f420a
--- /dev/null
+++ b/data/ui/e-comp-editor.eui
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/ui/e-html-editor.eui b/data/ui/e-html-editor.eui
new file mode 100644
index 0000000000..cbff2e2a1a
--- /dev/null
+++ b/data/ui/e-html-editor.eui
@@ -0,0 +1,214 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/ui/evolution-calendars.eui b/data/ui/evolution-calendars.eui
new file mode 100644
index 0000000000..da6a0e0359
--- /dev/null
+++ b/data/ui/evolution-calendars.eui
@@ -0,0 +1,214 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/ui/evolution-calendars.ui b/data/ui/evolution-calendars.ui
deleted file mode 100644
index 7919602372..0000000000
--- a/data/ui/evolution-calendars.ui
+++ /dev/null
@@ -1,199 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/data/ui/evolution-composer.eui b/data/ui/evolution-composer.eui
new file mode 100644
index 0000000000..d35add81c8
--- /dev/null
+++ b/data/ui/evolution-composer.eui
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/ui/evolution-contacts.eui b/data/ui/evolution-contacts.eui
new file mode 100644
index 0000000000..b819b0becd
--- /dev/null
+++ b/data/ui/evolution-contacts.eui
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/ui/evolution-contacts.ui b/data/ui/evolution-contacts.ui
deleted file mode 100644
index 2cdba8aa1b..0000000000
--- a/data/ui/evolution-contacts.ui
+++ /dev/null
@@ -1,120 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/data/ui/evolution-mail-reader.eui b/data/ui/evolution-mail-reader.eui
new file mode 100644
index 0000000000..c6ac0a68a8
--- /dev/null
+++ b/data/ui/evolution-mail-reader.eui
@@ -0,0 +1,221 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/ui/evolution-mail-reader.ui b/data/ui/evolution-mail-reader.ui
deleted file mode 100644
index efa3da7af1..0000000000
--- a/data/ui/evolution-mail-reader.ui
+++ /dev/null
@@ -1,210 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/data/ui/evolution-mail-viewer.eui b/data/ui/evolution-mail-viewer.eui
new file mode 100644
index 0000000000..76868ba8aa
--- /dev/null
+++ b/data/ui/evolution-mail-viewer.eui
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/ui/evolution-mail-viewer.ui b/data/ui/evolution-mail-viewer.ui
deleted file mode 100644
index 8984e9f1b3..0000000000
--- a/data/ui/evolution-mail-viewer.ui
+++ /dev/null
@@ -1,210 +0,0 @@
-
-
-
-
-
-
diff --git a/data/ui/evolution-mail.eui b/data/ui/evolution-mail.eui
new file mode 100644
index 0000000000..8ec5dbd2c8
--- /dev/null
+++ b/data/ui/evolution-mail.eui
@@ -0,0 +1,216 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/ui/evolution-mail.ui b/data/ui/evolution-mail.ui
deleted file mode 100644
index 6ba4929f29..0000000000
--- a/data/ui/evolution-mail.ui
+++ /dev/null
@@ -1,213 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/data/ui/evolution-memos.eui b/data/ui/evolution-memos.eui
new file mode 100644
index 0000000000..c50dbad12f
--- /dev/null
+++ b/data/ui/evolution-memos.eui
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/ui/evolution-memos.ui b/data/ui/evolution-memos.ui
deleted file mode 100644
index 8810b91ce9..0000000000
--- a/data/ui/evolution-memos.ui
+++ /dev/null
@@ -1,87 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/data/ui/evolution-shell.eui b/data/ui/evolution-shell.eui
new file mode 100644
index 0000000000..5f22ea74e7
--- /dev/null
+++ b/data/ui/evolution-shell.eui
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/ui/evolution-shell.ui b/data/ui/evolution-shell.ui
deleted file mode 100644
index 477713dd5c..0000000000
--- a/data/ui/evolution-shell.ui
+++ /dev/null
@@ -1,97 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/data/ui/evolution-tasks.eui b/data/ui/evolution-tasks.eui
new file mode 100644
index 0000000000..6a1d58f74d
--- /dev/null
+++ b/data/ui/evolution-tasks.eui
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/ui/evolution-tasks.ui b/data/ui/evolution-tasks.ui
deleted file mode 100644
index cd5a2687e8..0000000000
--- a/data/ui/evolution-tasks.ui
+++ /dev/null
@@ -1,101 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/data/webkit/e-editor.js b/data/webkit/e-editor.js
index d4ee23a2cb..44960d1496 100644
--- a/data/webkit/e-editor.js
+++ b/data/webkit/e-editor.js
@@ -2625,7 +2625,7 @@ EvoEditor.applyFontReset = function(record, isUndo)
EvoEditor.replaceInheritFonts = function(undoRedoRecord, selectionUpdater, nodes)
{
- var ii;
+ var ii, changed = false;
if (!nodes)
nodes = document.querySelectorAll("FONT[face=inherit]");
@@ -2667,7 +2667,10 @@ EvoEditor.replaceInheritFonts = function(undoRedoRecord, selectionUpdater, nodes
}
node.remove();
+ changed = true;
} else {
+ if (node.hasAttribute ("face"))
+ changed = true;
node.removeAttribute("face");
}
@@ -2677,6 +2680,8 @@ EvoEditor.replaceInheritFonts = function(undoRedoRecord, selectionUpdater, nodes
if (undoRedoRecord && undoRedoRecord.changes)
undoRedoRecord.apply = EvoEditor.applyFontReset;
+
+ return changed;
}
EvoEditor.maybeReplaceInheritFonts = function()
@@ -2708,15 +2713,17 @@ EvoEditor.SetFontName = function(name)
if (!name || name == "")
name = "inherit";
- var record, selectionUpdater = EvoSelection.CreateUpdaterObject(), bodyFontFamily;
+ var record, selectionUpdater = EvoSelection.CreateUpdaterObject(), bodyFontFamily, changed = false;
// to workaround https://bugs.webkit.org/show_bug.cgi?id=204622
bodyFontFamily = document.body.style.fontFamily;
record = EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_GROUP, "SetFontName");
try {
- if (!document.getSelection().isCollapsed && bodyFontFamily)
+ if (!document.getSelection().isCollapsed && bodyFontFamily) {
document.body.style.fontFamily = "";
+ changed = TRUE;
+ }
document.execCommand("FontName", false, name);
@@ -2732,19 +2739,22 @@ EvoEditor.SetFontName = function(name)
subrecord = EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "SetFontName", null, null, EvoEditor.CLAIM_CONTENT_FLAG_NONE);
try {
- EvoEditor.replaceInheritFonts(subrecord, selectionUpdater);
+ changed = EvoEditor.replaceInheritFonts(subrecord, selectionUpdater);
selectionUpdater.restore();
} finally {
EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "SetFontName");
}
}
} finally {
- if (bodyFontFamily && document.body.style.fontFamily != bodyFontFamily)
+ if (bodyFontFamily && document.body.style.fontFamily != bodyFontFamily) {
document.body.style.fontFamily = bodyFontFamily;
+ changed = true;
+ }
EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_GROUP, "SetFontName");
EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
- EvoEditor.EmitContentChanged();
+ if (changed)
+ EvoEditor.EmitContentChanged();
EvoEditor.removeEmptyStyleAttribute(document.body);
}
diff --git a/docs/reference/evolution-shell/evolution-shell-docs.sgml.in b/docs/reference/evolution-shell/evolution-shell-docs.sgml.in
index 8778d8d784..f15f659a87 100644
--- a/docs/reference/evolution-shell/evolution-shell-docs.sgml.in
+++ b/docs/reference/evolution-shell/evolution-shell-docs.sgml.in
@@ -24,7 +24,6 @@
-
@@ -42,6 +41,10 @@
Index
+
+ Index of new symbols in 3.56
+
+
Index of new symbols in 3.54
diff --git a/docs/reference/evolution-util/evolution-util-docs.sgml.in b/docs/reference/evolution-util/evolution-util-docs.sgml.in
index 9d59c113d8..978a8f91e6 100644
--- a/docs/reference/evolution-util/evolution-util-docs.sgml.in
+++ b/docs/reference/evolution-util/evolution-util-docs.sgml.in
@@ -108,10 +108,8 @@
-
-
@@ -252,6 +250,15 @@
+
+ User Interface Manager
+
+
+
+
+
+
+
(Unsorted Sections)
@@ -286,7 +293,6 @@
-
@@ -301,7 +307,6 @@
-
@@ -333,6 +338,10 @@
Index
+
+ Index of new symbols in 3.56
+
+
Index of new symbols in 3.54
diff --git a/po/POTFILES.in b/po/POTFILES.in
index ed7d2c30df..fe1f349ce3 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -22,7 +22,6 @@ data/org.gnome.evolution.spamassassin.gschema.xml.in
data/org.gnome.evolution.text-highlight.gschema.xml.in
data/org.gnome.Evolution.appdata.xml.in.in
data/org.gnome.Evolution.desktop.in.in
-[type: gettext/glade]data/ui/evolution-mail-viewer.ui
[type: gettext/glade]data/ui/evolution-shortcuts.ui
data/views/addressbook/galview.xml
data/views/calendar/galview.xml
@@ -244,7 +243,6 @@ src/e-util/e-online-button.c
src/e-util/e-paned.c
src/e-util/e-passwords.c
src/e-util/e-plugin.c
-src/e-util/e-popup-action.c
src/e-util/e-preferences-window.c
src/e-util/e-print.c
src/e-util/e-proxy-editor.c
@@ -548,7 +546,6 @@ src/plugins/templates/templates.c
src/shell/e-shell-backend.c
src/shell/e-shell.c
src/shell/e-shell-content.c
-src/shell/e-shell-headerbar.c
src/shell/e-shell-migrate.c
src/shell/e-shell-searchbar.c
src/shell/e-shell-switcher.c
diff --git a/src/addressbook/gui/contact-editor/e-contact-editor.c b/src/addressbook/gui/contact-editor/e-contact-editor.c
index c68a80f79d..20bc012545 100644
--- a/src/addressbook/gui/contact-editor/e-contact-editor.c
+++ b/src/addressbook/gui/contact-editor/e-contact-editor.c
@@ -242,37 +242,12 @@ struct _EContactEditorPrivate
GtkWidget *fullname_dialog;
GtkWidget *categories_dialog;
- GtkUIManager *ui_manager;
+ EUIManager *ui_manager;
EFocusTracker *focus_tracker;
};
G_DEFINE_TYPE_WITH_PRIVATE (EContactEditor, e_contact_editor, EAB_TYPE_EDITOR)
-static GtkActionEntry undo_entries[] = {
-
- { "undo-menu",
- "Undo menu",
- NULL,
- NULL,
- NULL,
- NULL }, /* just a fake undo menu, to get shortcuts working */
-
- { "undo",
- "edit-undo",
- N_("_Undo"),
- "z",
- N_("Undo"),
- NULL }, /* Handled by EFocusTracker */
-
- { "redo",
- "edit-redo",
- N_("_Redo"),
- "y",
- N_("Redo"),
- NULL } /* Handled by EFocusTracker */
-
-};
-
static void
connect_closure_free (ConnectClosure *connect_closure)
{
@@ -5244,56 +5219,66 @@ e_contact_editor_init (EContactEditor *e_contact_editor)
static void
e_contact_editor_constructed (GObject *object)
{
- const gchar *ui =
- ""
- " "
- " "
- " "
- " "
- " "
- " "
- "";
+ static const gchar *eui =
+ ""
+ ""
+ "";
+
+ static const EUIActionEntry undo_entries[] = {
+ { "undo-menu",
+ NULL,
+ "Undo menu",
+ NULL,
+ NULL,
+ NULL }, /* just a fake undo menu, to get shortcuts working */
+
+ { "undo",
+ "edit-undo",
+ N_("_Undo"),
+ "z",
+ N_("Undo"),
+ NULL }, /* Handled by EFocusTracker */
+
+ { "redo",
+ "edit-redo",
+ N_("_Redo"),
+ "y",
+ N_("Redo"),
+ NULL } /* Handled by EFocusTracker */
+ };
+
EContactEditor *editor = E_CONTACT_EDITOR (object);
- GtkActionGroup *action_group;
- GtkAction *action;
- GError *error = NULL;
+ EUIAction *action;
/* Chain up to parent's method. */
G_OBJECT_CLASS (e_contact_editor_parent_class)->constructed (object);
editor->priv->focus_tracker = e_focus_tracker_new (GTK_WINDOW (editor->priv->app));
- editor->priv->ui_manager = gtk_ui_manager_new ();
+ editor->priv->ui_manager = e_ui_manager_new ();
gtk_window_add_accel_group (
GTK_WINDOW (editor->priv->app),
- gtk_ui_manager_get_accel_group (editor->priv->ui_manager));
+ e_ui_manager_get_accel_group (editor->priv->ui_manager));
e_signal_connect_notify (
editor->priv->focus_tracker, "notify::focus",
G_CALLBACK (contact_editor_focus_widget_changed_cb), editor);
- action_group = gtk_action_group_new ("undo");
- gtk_action_group_set_translation_domain (
- action_group, GETTEXT_PACKAGE);
- gtk_action_group_add_actions (
- action_group, undo_entries,
- G_N_ELEMENTS (undo_entries), editor);
- gtk_ui_manager_insert_action_group (
- editor->priv->ui_manager, action_group, 0);
+ e_ui_manager_add_actions_with_eui_data (editor->priv->ui_manager, "undo", GETTEXT_PACKAGE,
+ undo_entries, G_N_ELEMENTS (undo_entries), editor, eui);
- action = gtk_action_group_get_action (action_group, "undo");
+ e_ui_manager_add_action_groups_to_widget (editor->priv->ui_manager, editor->priv->app);
+
+ action = e_ui_manager_get_action (editor->priv->ui_manager, "undo");
e_focus_tracker_set_undo_action (editor->priv->focus_tracker, action);
- action = gtk_action_group_get_action (action_group, "redo");
+ action = e_ui_manager_get_action (editor->priv->ui_manager, "redo");
e_focus_tracker_set_redo_action (editor->priv->focus_tracker, action);
-
- g_object_unref (action_group);
-
- gtk_ui_manager_add_ui_from_string (editor->priv->ui_manager, ui, -1, &error);
- if (error != NULL) {
- g_warning ("%s: %s", G_STRFUNC, error->message);
- g_error_free (error);
- }
}
static void
diff --git a/src/addressbook/gui/widgets/e-addressbook-view.c b/src/addressbook/gui/widgets/e-addressbook-view.c
index 4a0a99c653..cf4aedcc19 100644
--- a/src/addressbook/gui/widgets/e-addressbook-view.c
+++ b/src/addressbook/gui/widgets/e-addressbook-view.c
@@ -956,7 +956,7 @@ addressbook_view_update_actions (ESelectable *selectable,
gint n_clipboard_targets)
{
EAddressbookView *view;
- GtkAction *action;
+ EUIAction *action;
GtkTargetList *target_list;
gboolean can_paste = FALSE;
gboolean source_is_editable;
@@ -980,32 +980,32 @@ addressbook_view_update_actions (ESelectable *selectable,
action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
sensitive = source_is_editable && (n_selected > 0);
tooltip = _("Cut selected contacts to the clipboard");
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, tooltip);
action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
sensitive = (n_selected > 0);
tooltip = _("Copy selected contacts to the clipboard");
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, tooltip);
action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
sensitive = source_is_editable && can_paste;
tooltip = _("Paste contacts from the clipboard");
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, tooltip);
action = e_focus_tracker_get_delete_selection_action (focus_tracker);
sensitive = source_is_editable && (n_selected > 0);
tooltip = _("Delete selected contacts");
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, tooltip);
action = e_focus_tracker_get_select_all_action (focus_tracker);
sensitive = (n_contacts > 0);
tooltip = _("Select all visible contacts");
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, tooltip);
}
static void
diff --git a/src/addressbook/gui/widgets/eab-contact-display.c b/src/addressbook/gui/widgets/eab-contact-display.c
index 349e5080d5..7a188b1478 100644
--- a/src/addressbook/gui/widgets/eab-contact-display.c
+++ b/src/addressbook/gui/widgets/eab-contact-display.c
@@ -57,18 +57,6 @@ enum {
LAST_SIGNAL
};
-static const gchar *ui =
-""
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-"";
-
static guint signals[LAST_SIGNAL];
G_DEFINE_TYPE_WITH_PRIVATE (EABContactDisplay, eab_contact_display, E_TYPE_WEB_VIEW)
@@ -90,9 +78,11 @@ contact_display_emit_send_message (EABContactDisplay *display,
}
static void
-action_contact_mailto_copy_cb (GtkAction *action,
- EABContactDisplay *display)
+action_contact_mailto_copy_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EABContactDisplay *display = user_data;
GtkClipboard *clipboard;
EWebView *web_view;
EContact *contact;
@@ -121,9 +111,11 @@ action_contact_mailto_copy_cb (GtkAction *action,
}
static void
-action_contact_send_message_cb (GtkAction *action,
- EABContactDisplay *display)
+action_contact_send_message_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EABContactDisplay *display = user_data;
EWebView *web_view;
const gchar *uri;
gint index;
@@ -136,23 +128,6 @@ action_contact_send_message_cb (GtkAction *action,
contact_display_emit_send_message (display, index);
}
-static GtkActionEntry internal_mailto_entries[] = {
-
- { "contact-mailto-copy",
- "edit-copy",
- N_("Copy _Email Address"),
- "c",
- N_("Copy the email address to the clipboard"),
- G_CALLBACK (action_contact_mailto_copy_cb) },
-
- { "contact-send-message",
- "mail-message-new",
- N_("_Send New Message To…"),
- NULL,
- N_("Send a mail message to this address"),
- G_CALLBACK (action_contact_send_message_cb) }
-};
-
static void
load_contact (EABContactDisplay *display)
{
@@ -340,15 +315,14 @@ contact_display_content_loaded_cb (EWebView *web_view,
static void
contact_display_update_actions (EWebView *web_view)
{
- GtkActionGroup *action_group;
+ EUIActionGroup *action_group;
gboolean scheme_is_internal_mailto;
gboolean visible;
const gchar *group_name;
const gchar *uri;
/* Chain up to parent's update_actions() method. */
- E_WEB_VIEW_CLASS (eab_contact_display_parent_class)->
- update_actions (web_view);
+ E_WEB_VIEW_CLASS (eab_contact_display_parent_class)->update_actions (web_view);
uri = e_web_view_get_selected_uri (web_view);
@@ -358,14 +332,14 @@ contact_display_update_actions (EWebView *web_view)
/* Override how EWebView treats internal-mailto URIs. */
group_name = "uri";
action_group = e_web_view_get_action_group (web_view, group_name);
- visible = gtk_action_group_get_visible (action_group);
+ visible = e_ui_action_group_get_visible (action_group);
visible &= !scheme_is_internal_mailto;
- gtk_action_group_set_visible (action_group, visible);
+ e_ui_action_group_set_visible (action_group, visible);
group_name = "internal-mailto";
visible = scheme_is_internal_mailto;
action_group = e_web_view_get_action_group (web_view, group_name);
- gtk_action_group_set_visible (action_group, visible);
+ e_ui_action_group_set_visible (action_group, visible);
}
static void
@@ -463,12 +437,38 @@ eab_contact_display_class_init (EABContactDisplayClass *class)
static void
eab_contact_display_init (EABContactDisplay *display)
{
+ static const gchar *eui =
+ ""
+ ""
+ ""
+ " "
+ ""
+ ""
+ " "
+ ""
+ ""
+ "";
+
+ static const EUIActionEntry internal_mailto_entries[] = {
+
+ { "contact-mailto-copy",
+ "edit-copy",
+ N_("Copy _Email Address"),
+ "c",
+ N_("Copy the email address to the clipboard"),
+ action_contact_mailto_copy_cb, NULL, NULL, NULL },
+
+ { "contact-send-message",
+ "mail-message-new",
+ N_("_Send New Message To…"),
+ NULL,
+ N_("Send a mail message to this address"),
+ action_contact_send_message_cb, NULL, NULL, NULL }
+ };
+
EWebView *web_view;
- GtkUIManager *ui_manager;
- GtkActionGroup *action_group;
+ EUIManager *ui_manager;
GSettings *settings;
- const gchar *domain = GETTEXT_PACKAGE;
- GError *error = NULL;
display->priv = eab_contact_display_get_instance_private (display);
@@ -486,21 +486,8 @@ eab_contact_display_init (EABContactDisplay *display)
web_view, "style-updated",
G_CALLBACK (load_contact), NULL);
- action_group = gtk_action_group_new ("internal-mailto");
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
- g_object_unref (action_group);
-
- gtk_action_group_add_actions (
- action_group, internal_mailto_entries,
- G_N_ELEMENTS (internal_mailto_entries), display);
-
- /* Because we are loading from a hard-coded string, there is
- * no chance of I/O errors. Failure here implies a malformed
- * UI definition. Full stop. */
- gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
- if (error != NULL)
- g_error ("%s", error->message);
+ e_ui_manager_add_actions_with_eui_data (ui_manager, "internal-mailto", NULL,
+ internal_mailto_entries, G_N_ELEMENTS (internal_mailto_entries), display, eui);
settings = e_util_ref_settings ("org.gnome.evolution.addressbook");
g_signal_connect_object (settings, "changed::preview-home-before-work",
diff --git a/src/calendar/gui/e-calendar-view.c b/src/calendar/gui/e-calendar-view.c
index 8a2a6e7a44..fee35afdf6 100644
--- a/src/calendar/gui/e-calendar-view.c
+++ b/src/calendar/gui/e-calendar-view.c
@@ -420,7 +420,7 @@ calendar_view_update_actions (ESelectable *selectable,
gint n_clipboard_targets)
{
ECalendarView *view;
- GtkAction *action;
+ EUIAction *action;
GtkTargetList *target_list;
GSList *selected, *link;
gboolean can_paste = FALSE;
@@ -463,26 +463,26 @@ calendar_view_update_actions (ESelectable *selectable,
action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
sensitive = (n_selected > 0) && sources_are_editable && !is_editing;
tooltip = _("Cut selected events to the clipboard");
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, tooltip);
action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
sensitive = (n_selected > 0) && !is_editing;
tooltip = _("Copy selected events to the clipboard");
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, tooltip);
action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
sensitive = sources_are_editable && can_paste && !is_editing;
tooltip = _("Paste events from the clipboard");
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, tooltip);
action = e_focus_tracker_get_delete_selection_action (focus_tracker);
sensitive = (n_selected > 0) && sources_are_editable && !recurring && !is_editing;
tooltip = _("Delete selected events");
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, tooltip);
}
static void
diff --git a/src/calendar/gui/e-comp-editor-event.c b/src/calendar/gui/e-comp-editor-event.c
index 675f899c30..873f646aca 100644
--- a/src/calendar/gui/e-comp-editor-event.c
+++ b/src/calendar/gui/e-comp-editor-event.c
@@ -52,16 +52,6 @@ struct _ECompEditorEventPrivate {
G_DEFINE_TYPE_WITH_PRIVATE (ECompEditorEvent, e_comp_editor_event, E_TYPE_COMP_EDITOR)
-static void
-ece_event_action_classification_cb (GtkRadioAction *action,
- GtkRadioAction *current,
- ECompEditorEvent *event_editor)
-{
- g_return_if_fail (E_IS_COMP_EDITOR_EVENT (event_editor));
-
- e_comp_editor_set_changed (E_COMP_EDITOR (event_editor), TRUE);
-}
-
static void
ece_event_update_times (ECompEditorEvent *event_editor,
EDateEdit *date_edit,
@@ -166,7 +156,7 @@ ece_event_sensitize_widgets (ECompEditor *comp_editor,
{
ECompEditorEvent *event_editor;
gboolean is_organizer;
- GtkAction *action;
+ EUIAction *action;
GtkWidget *widget;
guint32 flags;
@@ -197,18 +187,18 @@ ece_event_sensitize_widgets (ECompEditor *comp_editor,
#undef sensitize_part
action = e_comp_editor_get_action (comp_editor, "all-day-event");
- gtk_action_set_sensitive (action, !force_insensitive);
+ e_ui_action_set_sensitive (action, !force_insensitive);
/* Disable radio items, instead of the whole submenu,
to see the value with read-only events/calendars. */
action = e_comp_editor_get_action (comp_editor, "classify-private");
- gtk_action_set_sensitive (action, !force_insensitive);
+ e_ui_action_set_sensitive (action, !force_insensitive);
action = e_comp_editor_get_action (comp_editor, "classify-confidential");
- gtk_action_set_sensitive (action, !force_insensitive);
+ e_ui_action_set_sensitive (action, !force_insensitive);
action = e_comp_editor_get_action (comp_editor, "classify-public");
- gtk_action_set_sensitive (action, !force_insensitive);
+ e_ui_action_set_sensitive (action, !force_insensitive);
if (event_editor->priv->insensitive_info_alert)
e_alert_response (event_editor->priv->insensitive_info_alert, GTK_RESPONSE_OK);
@@ -387,10 +377,10 @@ ece_event_update_timezone (ECompEditorEvent *event_editor,
(g_strcmp0 (i_cal_timezone_get_location (zone), i_cal_timezone_get_location (cfg_zone)) != 0 ||
g_strcmp0 (i_cal_timezone_get_tzid (zone), i_cal_timezone_get_tzid (cfg_zone)) != 0)) {
/* Show timezone part */
- GtkAction *action;
+ EUIAction *action;
action = e_comp_editor_get_action (comp_editor, "view-timezone");
- gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
+ e_ui_action_set_active (action, TRUE);
}
}
@@ -410,10 +400,10 @@ ece_event_fill_widgets (ECompEditor *comp_editor,
ICalComponent *component)
{
ECompEditorEvent *event_editor;
+ EUIAction *action;
ICalTime *dtstart, *dtend;
ICalProperty *prop;
gboolean all_day_event = FALSE;
- GtkAction *action;
guint32 flags;
g_return_if_fail (E_IS_COMP_EDITOR_EVENT (comp_editor));
@@ -513,7 +503,7 @@ ece_event_fill_widgets (ECompEditor *comp_editor,
g_object_unref (settings);
}
- gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
+ e_ui_action_set_active (action, TRUE);
g_clear_object (&dtstart);
g_clear_object (&dtend);
@@ -537,10 +527,10 @@ ece_event_fill_component (ECompEditor *comp_editor,
ICalComponent *component)
{
ECompEditorEvent *event_editor;
- gboolean date_valid, time_valid;
ICalProperty *dtstart_prop, *dtend_prop;
ICalProperty *prop;
ICalProperty_Class class_value;
+ gboolean date_valid, time_valid;
g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE);
g_return_val_if_fail (I_CAL_IS_COMPONENT (component), FALSE);
@@ -637,11 +627,9 @@ ece_event_fill_component (ECompEditor *comp_editor,
g_clear_object (&dtstart_prop);
g_clear_object (&dtend_prop);
- if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (
- e_comp_editor_get_action (comp_editor, "classify-private"))))
+ if (e_ui_action_get_active (e_comp_editor_get_action (comp_editor, "classify-private")))
class_value = I_CAL_CLASS_PRIVATE;
- else if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (
- e_comp_editor_get_action (comp_editor, "classify-confidential"))))
+ else if (e_ui_action_get_active (e_comp_editor_get_action (comp_editor, "classify-confidential")))
class_value = I_CAL_CLASS_CONFIDENTIAL;
else
class_value = I_CAL_CLASS_PUBLIC;
@@ -674,7 +662,7 @@ ece_event_notify_target_client_cb (GObject *object,
gpointer user_data)
{
ECompEditorEvent *event_editor;
- GtkAction *action;
+ EUIAction *action;
g_return_if_fail (E_IS_COMP_EDITOR_EVENT (object));
@@ -710,10 +698,10 @@ transform_toggle_to_timezone_visible_cb (GBinding *binding,
gpointer user_data)
{
ECompEditor *comp_editor = user_data;
- GtkToggleAction *action = GTK_TOGGLE_ACTION (e_comp_editor_get_action (comp_editor, "view-timezone"));
+ EUIAction *action = e_comp_editor_get_action (comp_editor, "view-timezone");
g_value_set_boolean (to_value,
- gtk_toggle_action_get_active (action) &&
+ e_ui_action_get_active (action) &&
(!g_value_get_boolean (from_value) || ece_event_client_needs_all_day_as_time (comp_editor)));
return TRUE;
@@ -734,129 +722,123 @@ transform_all_day_check_to_action_sensitive_cb (GBinding *binding,
return TRUE;
}
+static void
+ece_event_classification_radio_set_state_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
+{
+ ECompEditor *self = user_data;
+
+ g_return_if_fail (E_IS_COMP_EDITOR_EVENT (self));
+
+ e_ui_action_set_state (action, parameter);
+
+ e_comp_editor_set_changed (self, TRUE);
+}
+
static void
ece_event_setup_ui (ECompEditorEvent *event_editor)
{
- const gchar *ui =
- ""
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " \n"
- " \n"
- " \n"
- " "
- " "
- "";
+ static const gchar *eui =
+ ""
+ ""
+ ""
+ ""
+ " "
+ " "
+ ""
+ ""
+ ""
+ ""
+ " "
+ " "
+ ""
+ " "
+ " "
+ " "
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ "";
- const GtkToggleActionEntry view_actions[] = {
+ static const EUIActionEntry entries[] = {
{ "view-categories",
NULL,
N_("_Categories"),
NULL,
N_("Toggles whether to display categories"),
- NULL,
- FALSE },
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
{ "view-timezone",
"stock_timezone",
N_("Time _Zone"),
NULL,
N_("Toggles whether the time zone is displayed"),
- NULL,
- FALSE },
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
{ "all-day-event",
"stock_new-24h-appointment",
N_("All _Day Event"),
"Y",
N_("Toggles whether to have All Day Event"),
- NULL,
- FALSE },
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
{ "show-time-busy",
"dialog-error",
N_("Show Time as _Busy"),
NULL,
N_("Toggles whether to show time as busy"),
- NULL,
- FALSE }
- };
-
- const GtkRadioActionEntry classification_radio_entries[] = {
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
{ "classify-public",
NULL,
N_("Pu_blic"),
NULL,
N_("Classify as public"),
- I_CAL_CLASS_PUBLIC },
+ NULL, "s", "'public'", ece_event_classification_radio_set_state_cb },
{ "classify-private",
NULL,
N_("_Private"),
NULL,
N_("Classify as private"),
- I_CAL_CLASS_PRIVATE },
+ NULL, "s", "'private'", ece_event_classification_radio_set_state_cb },
{ "classify-confidential",
NULL,
N_("_Confidential"),
NULL,
N_("Classify as confidential"),
- I_CAL_CLASS_CONFIDENTIAL }
+ NULL, "s", "'confidential'", ece_event_classification_radio_set_state_cb }
};
ECompEditor *comp_editor;
GSettings *settings;
- GtkUIManager *ui_manager;
- GtkAction *action;
- GtkActionGroup *action_group;
+ EUIManager *ui_manager;
+ EUIAction *action;
GtkWidget *widget;
- GError *error = NULL;
g_return_if_fail (E_IS_COMP_EDITOR_EVENT (event_editor));
comp_editor = E_COMP_EDITOR (event_editor);
settings = e_comp_editor_get_settings (comp_editor);
ui_manager = e_comp_editor_get_ui_manager (comp_editor);
- action_group = e_comp_editor_get_action_group (comp_editor, "individual");
- gtk_action_group_add_toggle_actions (action_group,
- view_actions, G_N_ELEMENTS (view_actions), event_editor);
-
- gtk_action_group_add_radio_actions (
- action_group, classification_radio_entries,
- G_N_ELEMENTS (classification_radio_entries),
- I_CAL_CLASS_PUBLIC,
- G_CALLBACK (ece_event_action_classification_cb), event_editor);
-
- gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
-
- e_plugin_ui_register_manager (ui_manager, "org.gnome.evolution.event-editor", event_editor);
- e_plugin_ui_enable_manager (ui_manager, "org.gnome.evolution.event-editor");
-
- if (error) {
- g_critical ("%s: %s", G_STRFUNC, error->message);
- g_error_free (error);
- }
+ e_ui_manager_add_actions_with_eui_data (ui_manager, "individual", GETTEXT_PACKAGE,
+ entries, G_N_ELEMENTS (entries), event_editor, eui);
action = e_comp_editor_get_action (comp_editor, "view-categories");
e_binding_bind_property (
diff --git a/src/calendar/gui/e-comp-editor-memo.c b/src/calendar/gui/e-comp-editor-memo.c
index d554280206..a63eaddab0 100644
--- a/src/calendar/gui/e-comp-editor-memo.c
+++ b/src/calendar/gui/e-comp-editor-memo.c
@@ -123,7 +123,7 @@ ece_memo_notify_target_client_cb (GObject *object,
ECompEditorMemo *memo_editor;
ECompEditor *comp_editor;
ECalClient *cal_client;
- GtkAction *action;
+ EUIAction *action;
GtkWidget *description_widget;
GtkTextBuffer *text_buffer;
gboolean supports_date;
@@ -163,10 +163,10 @@ ece_memo_notify_target_client_cb (GObject *object,
gtk_widget_hide (GTK_WIDGET (memo_editor->priv->attachments_page));
action = e_comp_editor_get_action (comp_editor, "view-categories");
- gtk_action_set_sensitive (action, FALSE);
+ e_ui_action_set_sensitive (action, FALSE);
action = e_comp_editor_get_action (comp_editor, "option-attendees");
- gtk_action_set_visible (action, FALSE);
+ e_ui_action_set_visible (action, FALSE);
} else {
supports_date = !cal_client || !e_client_check_capability (E_CLIENT (cal_client), E_CAL_STATIC_CAPABILITY_NO_MEMO_START_DATE);
@@ -185,10 +185,10 @@ ece_memo_notify_target_client_cb (GObject *object,
gtk_widget_show (GTK_WIDGET (memo_editor->priv->attachments_page));
action = e_comp_editor_get_action (comp_editor, "view-categories");
- gtk_action_set_sensitive (action, TRUE);
+ e_ui_action_set_sensitive (action, TRUE);
action = e_comp_editor_get_action (comp_editor, "option-attendees");
- gtk_action_set_visible (action, TRUE);
+ e_ui_action_set_visible (action, TRUE);
}
}
@@ -241,53 +241,39 @@ ece_memo_sensitize_widgets (ECompEditor *comp_editor,
static void
ece_memo_setup_ui (ECompEditorMemo *memo_editor)
{
- const gchar *ui =
- ""
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- "";
+ static const gchar *eui =
+ ""
+ ""
+ ""
+ ""
+ " "
+ ""
+ ""
+ ""
+ "";
- const GtkToggleActionEntry view_actions[] = {
+ static const EUIActionEntry view_actions[] = {
{ "view-categories",
NULL,
N_("_Categories"),
NULL,
N_("Toggles whether to display categories"),
- NULL,
- FALSE }
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state }
};
ECompEditor *comp_editor;
GSettings *settings;
- GtkUIManager *ui_manager;
- GtkAction *action;
- GtkActionGroup *action_group;
- GError *error = NULL;
+ EUIManager *ui_manager;
+ EUIAction *action;
g_return_if_fail (E_IS_COMP_EDITOR_MEMO (memo_editor));
comp_editor = E_COMP_EDITOR (memo_editor);
settings = e_comp_editor_get_settings (comp_editor);
ui_manager = e_comp_editor_get_ui_manager (comp_editor);
- action_group = e_comp_editor_get_action_group (comp_editor, "individual");
- gtk_action_group_add_toggle_actions (action_group,
- view_actions, G_N_ELEMENTS (view_actions), memo_editor);
-
- gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
-
- e_plugin_ui_register_manager (ui_manager, "org.gnome.evolution.memo-editor", memo_editor);
- e_plugin_ui_enable_manager (ui_manager, "org.gnome.evolution.memo-editor");
-
- if (error) {
- g_critical ("%s: %s", G_STRFUNC, error->message);
- g_error_free (error);
- }
+ e_ui_manager_add_actions_with_eui_data (ui_manager, "individual", GETTEXT_PACKAGE,
+ view_actions, G_N_ELEMENTS (view_actions), memo_editor, eui);
action = e_comp_editor_get_action (comp_editor, "view-categories");
e_binding_bind_property (
diff --git a/src/calendar/gui/e-comp-editor-page-attachments.c b/src/calendar/gui/e-comp-editor-page-attachments.c
index b4ca64ea42..2326e55332 100644
--- a/src/calendar/gui/e-comp-editor-page-attachments.c
+++ b/src/calendar/gui/e-comp-editor-page-attachments.c
@@ -88,9 +88,11 @@ ecep_before_properties_popup_cb (EAttachmentView *view,
}
static void
-ecep_attachments_action_attach_cb (GtkAction *action,
- ECompEditorPageAttachments *page_attachments)
+ecep_attachments_action_attach_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ ECompEditorPageAttachments *page_attachments = user_data;
ECompEditor *comp_editor;
EAttachmentStore *store;
@@ -105,9 +107,12 @@ ecep_attachments_action_attach_cb (GtkAction *action,
}
static void
-ecep_attachments_select_page_cb (GtkAction *action,
- ECompEditorPage *page)
+ecep_attachments_select_page_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ ECompEditorPage *page = user_data;
+
g_return_if_fail (E_IS_COMP_EDITOR_PAGE_ATTACHMENTS (page));
e_comp_editor_page_select (page);
@@ -256,7 +261,7 @@ ecep_attachments_sensitize_widgets (ECompEditorPage *page,
gboolean force_insensitive)
{
ECompEditor *comp_editor;
- GtkAction *action;
+ EUIAction *action;
g_return_if_fail (E_IS_COMP_EDITOR_PAGE_ATTACHMENTS (page));
@@ -265,7 +270,7 @@ ecep_attachments_sensitize_widgets (ECompEditorPage *page,
comp_editor = e_comp_editor_page_ref_editor (page);
action = e_comp_editor_get_action (comp_editor, "attachments-attach");
- gtk_action_set_sensitive (action, !force_insensitive);
+ e_ui_action_set_sensitive (action, !force_insensitive);
g_clear_object (&comp_editor);
}
@@ -748,80 +753,67 @@ ecep_attachments_dispose (GObject *object)
static void
ecep_attachments_setup_ui (ECompEditorPageAttachments *page_attachments)
{
- const gchar *ui =
- ""
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- "";
+ static const gchar *eui =
+ ""
+ ""
+ ""
+ " "
+ ""
+ ""
+ ""
+ " "
+ ""
+ ""
+ ""
+ "";
- GtkActionEntry editable_entries[] = {
+ static const EUIActionEntry editable_entries[] = {
{ "attachments-attach",
"mail-attachment",
N_("_Attachment…"),
"m",
N_("Attach a file"),
- G_CALLBACK (ecep_attachments_action_attach_cb) }
+ ecep_attachments_action_attach_cb, NULL, NULL, NULL }
};
- GtkActionEntry options_entries[] = {
+ static const EUIActionEntry options_entries[] = {
{ "page-attachments",
"mail-attachment",
N_("_Attachments"),
NULL,
N_("Show attachments"),
- G_CALLBACK (ecep_attachments_select_page_cb) }
+ ecep_attachments_select_page_cb, NULL, NULL, NULL }
};
ECompEditor *comp_editor;
- GtkUIManager *ui_manager;
- GtkActionGroup *action_group;
- GtkAction *action;
- GError *error = NULL;
+ EUIManager *ui_manager;
+ EUIAction *action;
g_return_if_fail (E_IS_COMP_EDITOR_PAGE_ATTACHMENTS (page_attachments));
comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_attachments));
ui_manager = e_comp_editor_get_ui_manager (comp_editor);
- action_group = e_comp_editor_get_action_group (comp_editor, "editable");
- gtk_action_group_add_actions (
- action_group, editable_entries,
- G_N_ELEMENTS (editable_entries), page_attachments);
+ e_ui_manager_add_actions (ui_manager, "editable", GETTEXT_PACKAGE,
+ editable_entries, G_N_ELEMENTS (editable_entries), page_attachments);
- action = gtk_action_group_get_action (action_group, "attachments-attach");
+ action = e_comp_editor_get_action (comp_editor, "attachments-attach");
e_binding_bind_property (
page_attachments, "visible",
action, "visible",
G_BINDING_SYNC_CREATE);
- action_group = e_comp_editor_get_action_group (comp_editor, "individual");
+ e_ui_manager_add_actions_with_eui_data (ui_manager, "individual", GETTEXT_PACKAGE,
+ options_entries, G_N_ELEMENTS (options_entries), page_attachments, eui);
- gtk_action_group_add_actions (
- action_group, options_entries,
- G_N_ELEMENTS (options_entries), page_attachments);
-
- action = gtk_action_group_get_action (action_group, "page-attachments");
+ action = e_comp_editor_get_action (comp_editor, "page-attachments");
e_binding_bind_property (
page_attachments, "visible",
action, "visible",
G_BINDING_SYNC_CREATE);
- gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
- if (error != NULL) {
- g_warning ("%s: Failed to add UI from string: %s", G_STRFUNC, error->message);
- g_error_free (error);
- }
-
g_clear_object (&comp_editor);
}
@@ -830,11 +822,11 @@ ecep_attachments_constructed (GObject *object)
{
ECompEditorPageAttachments *page_attachments;
ECompEditor *comp_editor;
+ EUIAction *action;
GSettings *settings;
GtkSizeGroup *size_group;
GtkWidget *container;
GtkWidget *widget;
- GtkAction *action;
G_OBJECT_CLASS (e_comp_editor_page_attachments_parent_class)->constructed (object);
@@ -939,7 +931,7 @@ ecep_attachments_constructed (GObject *object)
widget = gtk_button_new ();
action = e_attachment_view_get_action (E_ATTACHMENT_VIEW (page_attachments->priv->icon_view), "add-uri");
gtk_button_set_image (GTK_BUTTON (widget), gtk_image_new ());
- gtk_activatable_set_related_action (GTK_ACTIVATABLE (widget), action);
+ e_ui_action_util_assign_to_widget (action, widget);
gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0);
gtk_widget_show (widget);
@@ -950,7 +942,7 @@ ecep_attachments_constructed (GObject *object)
widget = gtk_button_new ();
action = e_attachment_view_get_action (E_ATTACHMENT_VIEW (page_attachments->priv->icon_view), "add");
gtk_button_set_image (GTK_BUTTON (widget), gtk_image_new ());
- gtk_activatable_set_related_action (GTK_ACTIVATABLE (widget), action);
+ e_ui_action_util_assign_to_widget (action, widget);
gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0);
gtk_widget_show (widget);
diff --git a/src/calendar/gui/e-comp-editor-page-general.c b/src/calendar/gui/e-comp-editor-page-general.c
index 70806c284d..a91172cd78 100644
--- a/src/calendar/gui/e-comp-editor-page-general.c
+++ b/src/calendar/gui/e-comp-editor-page-general.c
@@ -92,43 +92,63 @@ ecep_general_set_column_visible (ECompEditorPageGeneral *page_general,
}
static void
-action_view_role_cb (GtkToggleAction *action,
- ECompEditorPageGeneral *page_general)
+action_view_role_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ ECompEditorPageGeneral *page_general = user_data;
+
g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+ e_ui_action_set_active (action, !e_ui_action_get_active (action));
+
ecep_general_set_column_visible (page_general, E_MEETING_STORE_ROLE_COL,
- gtk_toggle_action_get_active (action));
+ e_ui_action_get_active (action));
}
static void
-action_view_rsvp_cb (GtkToggleAction *action,
- ECompEditorPageGeneral *page_general)
+action_view_rsvp_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ ECompEditorPageGeneral *page_general = user_data;
+
g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+ e_ui_action_set_active (action, !e_ui_action_get_active (action));
+
ecep_general_set_column_visible (page_general, E_MEETING_STORE_RSVP_COL,
- gtk_toggle_action_get_active (action));
+ e_ui_action_get_active (action));
}
static void
-action_view_status_cb (GtkToggleAction *action,
- ECompEditorPageGeneral *page_general)
+action_view_status_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ ECompEditorPageGeneral *page_general = user_data;
+
g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+ e_ui_action_set_active (action, !e_ui_action_get_active (action));
+
ecep_general_set_column_visible (page_general, E_MEETING_STORE_STATUS_COL,
- gtk_toggle_action_get_active (action));
+ e_ui_action_get_active (action));
}
static void
-action_view_type_cb (GtkToggleAction *action,
- ECompEditorPageGeneral *page_general)
+action_view_type_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ ECompEditorPageGeneral *page_general = user_data;
+
g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+ e_ui_action_set_active (action, !e_ui_action_get_active (action));
+
ecep_general_set_column_visible (page_general, E_MEETING_STORE_TYPE_COL,
- gtk_toggle_action_get_active (action));
+ e_ui_action_get_active (action));
}
static void
@@ -682,76 +702,67 @@ static void
ecep_general_init_ui (ECompEditorPageGeneral *page_general,
ECompEditor *comp_editor)
{
- const gchar *ui =
- ""
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- "";
-
- const GtkToggleActionEntry attendees_toggle_entry[] = {
+ static const gchar *eui =
+ ""
+ ""
+ ""
+ ""
+ " "
+ " "
+ " "
+ " "
+ ""
+ ""
+ ""
+ ""
+ " "
+ ""
+ ""
+ ""
+ "";
+ static const EUIActionEntry attendees_toggle_entry[] = {
{ "option-attendees",
NULL,
N_("A_ttendees"),
NULL,
N_("Toggles whether the Attendees are displayed"),
- NULL,
- FALSE }
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state }
};
- const GtkToggleActionEntry columns_toggle_entries[] = {
-
+ static const EUIActionEntry columns_toggle_entries[] = {
{ "view-role",
NULL,
N_("R_ole Field"),
NULL,
N_("Toggles whether the Role field is displayed"),
- G_CALLBACK (action_view_role_cb),
- TRUE },
+ NULL, NULL, "true", action_view_role_cb },
{ "view-rsvp",
NULL,
N_("_RSVP"),
NULL,
N_("Toggles whether the RSVP field is displayed"),
- G_CALLBACK (action_view_rsvp_cb),
- TRUE },
+ NULL, NULL, "true", action_view_rsvp_cb },
{ "view-status",
NULL,
N_("_Status Field"),
NULL,
N_("Toggles whether the Status field is displayed"),
- G_CALLBACK (action_view_status_cb),
- TRUE },
+ NULL, NULL, "true", action_view_status_cb },
{ "view-type",
NULL,
N_("_Type Field"),
NULL,
N_("Toggles whether the Attendee Type is displayed"),
- G_CALLBACK (action_view_type_cb),
- TRUE }
+ NULL, NULL, "true", action_view_type_cb }
};
- GtkUIManager *ui_manager;
- GtkActionGroup *action_group;
- GtkAction *action;
+ EUIManager *ui_manager;
+ EUIAction *action;
GSettings *settings;
- GError *error = NULL;
g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
@@ -759,33 +770,16 @@ ecep_general_init_ui (ECompEditorPageGeneral *page_general,
settings = e_comp_editor_get_settings (comp_editor);
ui_manager = e_comp_editor_get_ui_manager (comp_editor);
- action_group = gtk_action_group_new ("columns");
- gtk_action_group_set_translation_domain (
- action_group, GETTEXT_PACKAGE);
- gtk_action_group_add_toggle_actions (
- action_group, columns_toggle_entries,
- G_N_ELEMENTS (columns_toggle_entries), page_general);
- gtk_ui_manager_insert_action_group (
- ui_manager, action_group, 0);
+ e_ui_manager_add_actions (ui_manager, "columns", GETTEXT_PACKAGE,
+ columns_toggle_entries, G_N_ELEMENTS (columns_toggle_entries), page_general);
e_binding_bind_property (
page_general, "show-attendees",
- action_group, "sensitive",
+ e_ui_manager_get_action_group (ui_manager, "columns"), "sensitive",
G_BINDING_SYNC_CREATE);
- g_object_unref (action_group);
-
- action_group = e_comp_editor_get_action_group (comp_editor, "individual");
- gtk_action_group_add_toggle_actions (
- action_group, attendees_toggle_entry,
- G_N_ELEMENTS (attendees_toggle_entry), page_general);
-
- gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
-
- if (error) {
- g_critical ("%s: %s", G_STRFUNC, error->message);
- g_error_free (error);
- }
+ e_ui_manager_add_actions_with_eui_data (ui_manager, "individual", GETTEXT_PACKAGE,
+ attendees_toggle_entry, G_N_ELEMENTS (attendees_toggle_entry), page_general, eui);
action = e_comp_editor_get_action (comp_editor, "option-attendees");
e_binding_bind_property (
@@ -798,24 +792,32 @@ ecep_general_init_ui (ECompEditorPageGeneral *page_general,
settings, "editor-show-role",
action, "active",
G_SETTINGS_BIND_DEFAULT);
+ ecep_general_set_column_visible (page_general, E_MEETING_STORE_ROLE_COL,
+ e_ui_action_get_active (action));
action = e_comp_editor_get_action (comp_editor, "view-rsvp");
g_settings_bind (
settings, "editor-show-rsvp",
action, "active",
G_SETTINGS_BIND_DEFAULT);
+ ecep_general_set_column_visible (page_general, E_MEETING_STORE_RSVP_COL,
+ e_ui_action_get_active (action));
action = e_comp_editor_get_action (comp_editor, "view-status");
g_settings_bind (
settings, "editor-show-status",
action, "active",
G_SETTINGS_BIND_DEFAULT);
+ ecep_general_set_column_visible (page_general, E_MEETING_STORE_STATUS_COL,
+ e_ui_action_get_active (action));
action = e_comp_editor_get_action (comp_editor, "view-type");
g_settings_bind (
settings, "editor-show-type",
action, "active",
G_SETTINGS_BIND_DEFAULT);
+ ecep_general_set_column_visible (page_general, E_MEETING_STORE_TYPE_COL,
+ e_ui_action_get_active (action));
}
static void
@@ -824,10 +826,10 @@ ecep_general_sensitize_widgets (ECompEditorPage *page,
{
ECompEditorPageGeneral *page_general;
GtkTreeSelection *selection;
- GtkAction *action;
gboolean sensitive, organizer_is_user, delegate, delegate_to_many = FALSE, read_only = TRUE, any_selected = FALSE;
ECompEditor *comp_editor;
ECalClient *client;
+ EUIAction *action;
guint32 flags;
g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page));
@@ -871,7 +873,7 @@ ecep_general_sensitize_widgets (ECompEditorPage *page,
gtk_widget_set_sensitive (page_general->priv->attendees_list_view, !read_only && !force_insensitive);
action = e_comp_editor_get_action (comp_editor, "option-attendees");
- gtk_action_set_sensitive (action, !force_insensitive && !read_only);
+ e_ui_action_set_sensitive (action, !force_insensitive && !read_only);
if (page_general->priv->comp_color &&
!e_comp_editor_property_part_get_sensitize_handled (page_general->priv->comp_color)) {
diff --git a/src/calendar/gui/e-comp-editor-page-recurrence.c b/src/calendar/gui/e-comp-editor-page-recurrence.c
index 5355755e2a..1a4482b764 100644
--- a/src/calendar/gui/e-comp-editor-page-recurrence.c
+++ b/src/calendar/gui/e-comp-editor-page-recurrence.c
@@ -2010,9 +2010,12 @@ ecep_recurrence_fill_component (ECompEditorPage *page,
}
static void
-ecep_recurrence_select_page_cb (GtkAction *action,
- ECompEditorPage *page)
+ecep_recurrence_select_page_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ ECompEditorPage *page = user_data;
+
g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page));
e_comp_editor_page_select (page);
@@ -2021,44 +2024,39 @@ ecep_recurrence_select_page_cb (GtkAction *action,
static void
ecep_recurrence_setup_ui (ECompEditorPageRecurrence *page_recurrence)
{
- const gchar *ui =
- ""
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- "";
+ static const gchar *eui =
+ ""
+ ""
+ ""
+ ""
+ " "
+ ""
+ ""
+ ""
+ "";
- const GtkActionEntry options_actions[] = {
+ static const EUIActionEntry options_actions[] = {
{ "page-recurrence",
"stock_task-recurring",
N_("R_ecurrence"),
NULL,
N_("Set or unset recurrence"),
- G_CALLBACK (ecep_recurrence_select_page_cb) }
+ ecep_recurrence_select_page_cb, NULL, NULL, NULL }
};
ECompEditor *comp_editor;
- GtkUIManager *ui_manager;
- GtkActionGroup *action_group;
- GtkAction *action;
- GError *error = NULL;
+ EUIManager *ui_manager;
+ EUIAction *action;
g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page_recurrence));
comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_recurrence));
ui_manager = e_comp_editor_get_ui_manager (comp_editor);
- action_group = e_comp_editor_get_action_group (comp_editor, "individual");
- gtk_action_group_add_actions (action_group,
- options_actions, G_N_ELEMENTS (options_actions), page_recurrence);
+ e_ui_manager_add_actions_with_eui_data (ui_manager, "individual", GETTEXT_PACKAGE,
+ options_actions, G_N_ELEMENTS (options_actions), page_recurrence, eui);
- gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
-
- action = gtk_action_group_get_action (action_group, "page-recurrence");
+ action = e_comp_editor_get_action (comp_editor, "page-recurrence");
if (action) {
e_binding_bind_property (
page_recurrence, "visible",
@@ -2067,11 +2065,6 @@ ecep_recurrence_setup_ui (ECompEditorPageRecurrence *page_recurrence)
}
g_clear_object (&comp_editor);
-
- if (error) {
- g_critical ("%s: %s", G_STRFUNC, error->message);
- g_error_free (error);
- }
}
static void
diff --git a/src/calendar/gui/e-comp-editor-page-reminders.c b/src/calendar/gui/e-comp-editor-page-reminders.c
index 68661b5683..2ff89a735f 100644
--- a/src/calendar/gui/e-comp-editor-page-reminders.c
+++ b/src/calendar/gui/e-comp-editor-page-reminders.c
@@ -1677,9 +1677,12 @@ ecep_reminders_fill_component (ECompEditorPage *page,
}
static void
-ecep_reminders_select_page_cb (GtkAction *action,
- ECompEditorPage *page)
+ecep_reminders_select_page_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ ECompEditorPage *page = user_data;
+
g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page));
e_comp_editor_page_select (page);
@@ -1688,44 +1691,39 @@ ecep_reminders_select_page_cb (GtkAction *action,
static void
ecep_reminders_setup_ui (ECompEditorPageReminders *page_reminders)
{
- const gchar *ui =
- ""
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- "";
+ static const gchar *eui =
+ ""
+ ""
+ ""
+ ""
+ " "
+ ""
+ ""
+ ""
+ "";
- const GtkActionEntry options_actions[] = {
+ static const EUIActionEntry options_actions[] = {
{ "page-reminders",
"appointment-soon",
N_("_Reminders"),
NULL,
N_("Set or unset reminders"),
- G_CALLBACK (ecep_reminders_select_page_cb) }
+ ecep_reminders_select_page_cb, NULL, NULL, NULL }
};
ECompEditor *comp_editor;
- GtkUIManager *ui_manager;
- GtkActionGroup *action_group;
- GtkAction *action;
- GError *error = NULL;
+ EUIManager *ui_manager;
+ EUIAction *action;
g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders));
comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_reminders));
ui_manager = e_comp_editor_get_ui_manager (comp_editor);
- action_group = e_comp_editor_get_action_group (comp_editor, "individual");
- gtk_action_group_add_actions (action_group,
- options_actions, G_N_ELEMENTS (options_actions), page_reminders);
+ e_ui_manager_add_actions_with_eui_data (ui_manager, "individual", GETTEXT_PACKAGE,
+ options_actions, G_N_ELEMENTS (options_actions), page_reminders, eui);
- gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
-
- action = gtk_action_group_get_action (action_group, "page-reminders");
+ action = e_comp_editor_get_action (comp_editor, "page-reminders");
if (action) {
e_binding_bind_property (
page_reminders, "visible",
@@ -1734,11 +1732,6 @@ ecep_reminders_setup_ui (ECompEditorPageReminders *page_reminders)
}
g_clear_object (&comp_editor);
-
- if (error) {
- g_critical ("%s: %s", G_STRFUNC, error->message);
- g_error_free (error);
- }
}
static gboolean
diff --git a/src/calendar/gui/e-comp-editor-page-schedule.c b/src/calendar/gui/e-comp-editor-page-schedule.c
index 6310b8b021..81e78abe88 100644
--- a/src/calendar/gui/e-comp-editor-page-schedule.c
+++ b/src/calendar/gui/e-comp-editor-page-schedule.c
@@ -477,9 +477,12 @@ e_comp_editor_page_schedule_get_property (GObject *object,
}
static void
-ecep_schedule_select_page_cb (GtkAction *action,
- ECompEditorPage *page)
+ecep_schedule_select_page_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ ECompEditorPage *page = user_data;
+
g_return_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page));
e_comp_editor_page_select (page);
@@ -488,47 +491,37 @@ ecep_schedule_select_page_cb (GtkAction *action,
static void
ecep_schedule_setup_ui (ECompEditorPageSchedule *page_schedule)
{
- const gchar *ui =
- ""
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- "";
+ static const gchar *eui =
+ ""
+ ""
+ ""
+ ""
+ " "
+ ""
+ ""
+ ""
+ "";
- const GtkActionEntry options_actions[] = {
+ static const EUIActionEntry options_actions[] = {
{ "page-schedule",
"query-free-busy",
N_("_Schedule"),
NULL,
N_("Query free / busy information for the attendees"),
- G_CALLBACK (ecep_schedule_select_page_cb) }
+ ecep_schedule_select_page_cb, NULL, NULL, NULL }
};
ECompEditor *comp_editor;
- GtkUIManager *ui_manager;
- GtkAction *action;
- GtkActionGroup *action_group;
- GError *error = NULL;
+ EUIManager *ui_manager;
+ EUIAction *action;
g_return_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page_schedule));
comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_schedule));
ui_manager = e_comp_editor_get_ui_manager (comp_editor);
- action_group = e_comp_editor_get_action_group (comp_editor, "individual");
- gtk_action_group_add_actions (action_group,
- options_actions, G_N_ELEMENTS (options_actions), page_schedule);
-
- gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
-
- if (error) {
- g_critical ("%s: %s", G_STRFUNC, error->message);
- g_error_free (error);
- }
+ e_ui_manager_add_actions_with_eui_data (ui_manager, "individual", GETTEXT_PACKAGE,
+ options_actions, G_N_ELEMENTS (options_actions), page_schedule, eui);
action = e_comp_editor_get_action (comp_editor, "page-schedule");
e_binding_bind_property (
diff --git a/src/calendar/gui/e-comp-editor-task.c b/src/calendar/gui/e-comp-editor-task.c
index 010eb5a76b..5b93862ab9 100644
--- a/src/calendar/gui/e-comp-editor-task.c
+++ b/src/calendar/gui/e-comp-editor-task.c
@@ -190,10 +190,10 @@ ece_task_update_timezone (ECompEditorTask *task_editor,
(g_strcmp0 (i_cal_timezone_get_location (zone), i_cal_timezone_get_location (cfg_zone)) != 0 ||
g_strcmp0 (i_cal_timezone_get_tzid (zone), i_cal_timezone_get_tzid (cfg_zone)) != 0)) {
/* Show timezone part */
- GtkAction *action;
+ EUIAction *action;
action = e_comp_editor_get_action (comp_editor, "view-timezone");
- gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
+ e_ui_action_set_active (action, TRUE);
}
}
}
@@ -216,8 +216,8 @@ ece_task_notify_target_client_cb (GObject *object,
ECompEditorTask *task_editor;
ECompEditor *comp_editor;
ECalClient *cal_client;
+ EUIAction *action;
GtkWidget *edit_widget;
- GtkAction *action;
gboolean date_only;
gboolean was_allday;
gboolean can_recur;
@@ -231,7 +231,7 @@ ece_task_notify_target_client_cb (GObject *object,
cal_client = e_comp_editor_get_target_client (comp_editor);
action = e_comp_editor_get_action (comp_editor, "all-day-task");
- was_allday = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
+ was_allday = e_ui_action_get_active (action);
date_only = !cal_client || e_client_check_capability (E_CLIENT (cal_client), E_CAL_STATIC_CAPABILITY_TASK_DATE_ONLY);
@@ -243,14 +243,14 @@ ece_task_notify_target_client_cb (GObject *object,
gtk_widget_set_sensitive (edit_widget, !date_only);
action = e_comp_editor_get_action (comp_editor, "view-timezone");
- gtk_action_set_sensitive (action, !date_only);
+ e_ui_action_set_sensitive (action, !date_only);
action = e_comp_editor_get_action (comp_editor, "all-day-task");
- gtk_action_set_visible (action, !date_only);
+ e_ui_action_set_visible (action, !date_only);
if (was_allday) {
action = e_comp_editor_get_action (comp_editor, "all-day-task");
- gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
+ e_ui_action_set_active (action, TRUE);
}
can_reminders = !cal_client || !e_client_check_capability (E_CLIENT (cal_client), E_CAL_STATIC_CAPABILITY_TASK_NO_ALARM);
@@ -531,7 +531,7 @@ ece_task_sensitize_widgets (ECompEditor *comp_editor,
gboolean force_insensitive)
{
ECompEditorTask *task_editor;
- GtkAction *action;
+ EUIAction *action;
gboolean is_organizer;
guint32 flags;
@@ -544,7 +544,7 @@ ece_task_sensitize_widgets (ECompEditor *comp_editor,
task_editor = E_COMP_EDITOR_TASK (comp_editor);
action = e_comp_editor_get_action (comp_editor, "all-day-task");
- gtk_action_set_sensitive (action, !force_insensitive);
+ e_ui_action_set_sensitive (action, !force_insensitive);
if (task_editor->priv->insensitive_info_alert)
e_alert_response (task_editor->priv->insensitive_info_alert, GTK_RESPONSE_OK);
@@ -592,10 +592,10 @@ ece_task_fill_widgets (ECompEditor *comp_editor,
E_COMP_EDITOR_CLASS (e_comp_editor_task_parent_class)->fill_widgets (comp_editor, component);
if (force_allday) {
- GtkAction *action;
+ EUIAction *action;
action = e_comp_editor_get_action (comp_editor, "all-day-task");
- gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
+ e_ui_action_set_active (action, TRUE);
}
}
@@ -774,81 +774,70 @@ ece_task_all_day_notify_active_cb (GObject *object,
static void
ece_task_setup_ui (ECompEditorTask *task_editor)
{
- const gchar *ui =
- ""
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " \n"
- " \n"
- " "
- " "
- "";
+ static const gchar *eui =
+ ""
+ ""
+ ""
+ ""
+ " "
+ " "
+ ""
+ ""
+ ""
+ ""
+ " "
+ ""
+ ""
+ ""
+ ""
+ ""
+ "";
- const GtkToggleActionEntry view_actions[] = {
+ static const EUIActionEntry view_actions[] = {
{ "view-categories",
NULL,
N_("_Categories"),
NULL,
N_("Toggles whether to display categories"),
- NULL,
- FALSE },
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
{ "view-timezone",
"stock_timezone",
N_("Time _Zone"),
NULL,
N_("Toggles whether the time zone is displayed"),
- NULL,
- FALSE },
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
{ "all-day-task",
"stock_new-24h-appointment",
N_("All _Day Task"),
"Y",
N_("Toggles whether to have All Day Task"),
- NULL,
- FALSE }
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state }
};
ECompEditor *comp_editor;
GSettings *settings;
- GtkUIManager *ui_manager;
- GtkAction *action;
- GtkActionGroup *action_group;
+ EUIManager *ui_manager;
+ EUIAction *action;
GtkWidget *edit_widget;
- GError *error = NULL;
g_return_if_fail (E_IS_COMP_EDITOR_TASK (task_editor));
comp_editor = E_COMP_EDITOR (task_editor);
settings = e_comp_editor_get_settings (comp_editor);
ui_manager = e_comp_editor_get_ui_manager (comp_editor);
- action_group = e_comp_editor_get_action_group (comp_editor, "individual");
- gtk_action_group_add_toggle_actions (action_group,
- view_actions, G_N_ELEMENTS (view_actions), task_editor);
-
- gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
-
- e_plugin_ui_register_manager (ui_manager, "org.gnome.evolution.task-editor", task_editor);
- e_plugin_ui_enable_manager (ui_manager, "org.gnome.evolution.task-editor");
-
- if (error) {
- g_critical ("%s: %s", G_STRFUNC, error->message);
- g_error_free (error);
- }
+ e_ui_manager_add_actions_with_eui_data (ui_manager, "individual", GETTEXT_PACKAGE,
+ view_actions, G_N_ELEMENTS (view_actions), task_editor, eui);
action = e_comp_editor_get_action (comp_editor, "view-timezone");
e_binding_bind_property (
diff --git a/src/calendar/gui/e-comp-editor.c b/src/calendar/gui/e-comp-editor.c
index eacd0c2cd8..5a4219b627 100644
--- a/src/calendar/gui/e-comp-editor.c
+++ b/src/calendar/gui/e-comp-editor.c
@@ -52,9 +52,10 @@ struct _ECompEditorPrivate {
guint32 flags;
EMenuBar *menu_bar;
+ GtkWidget *menu_button; /* owned by menu_bar */
EFocusTracker *focus_tracker;
- GtkUIManager *ui_manager;
+ EUIManager *ui_manager;
GSList *pages; /* ECompEditorPage * */
gulong show_attendees_handler_id;
@@ -130,11 +131,25 @@ ece_restore_focus (ECompEditor *comp_editor)
}
}
+static EUIActionGroup *
+ece_get_action_group (ECompEditor *comp_editor,
+ const gchar *group_name)
+{
+ EUIManager *ui_manager;
+
+ g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
+ g_return_val_if_fail (group_name != NULL, NULL);
+
+ ui_manager = e_comp_editor_get_ui_manager (comp_editor);
+
+ return e_ui_manager_get_action_group (ui_manager, group_name);
+}
+
static void
e_comp_editor_enable (ECompEditor *comp_editor,
gboolean enable)
{
- GtkActionGroup *group;
+ EUIActionGroup *group;
GtkWidget *current_focus;
g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
@@ -143,14 +158,14 @@ e_comp_editor_enable (ECompEditor *comp_editor,
gtk_widget_set_sensitive (GTK_WIDGET (comp_editor->priv->content), enable);
- group = e_comp_editor_get_action_group (comp_editor, "individual");
- gtk_action_group_set_sensitive (group, enable);
+ group = ece_get_action_group (comp_editor, "individual");
+ e_ui_action_group_set_sensitive (group, enable);
- group = e_comp_editor_get_action_group (comp_editor, "core");
- gtk_action_group_set_sensitive (group, enable);
+ group = ece_get_action_group (comp_editor, "core");
+ e_ui_action_group_set_sensitive (group, enable);
- group = e_comp_editor_get_action_group (comp_editor, "editable");
- gtk_action_group_set_sensitive (group, enable);
+ group = ece_get_action_group (comp_editor, "editable");
+ e_ui_action_group_set_sensitive (group, enable);
if (enable) {
e_comp_editor_sensitize_widgets (comp_editor);
@@ -1433,27 +1448,32 @@ e_comp_editor_prompt_and_save_changes (ECompEditor *comp_editor,
}
static void
-action_close_cb (GtkAction *action,
- ECompEditor *comp_editor)
+action_close_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
- g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+ ECompEditor *self = user_data;
- if (e_comp_editor_prompt_and_save_changes (comp_editor, TRUE))
- e_comp_editor_close (comp_editor);
+ g_return_if_fail (E_IS_COMP_EDITOR (self));
+
+ if (e_comp_editor_prompt_and_save_changes (self, TRUE))
+ e_comp_editor_close (self);
}
static void
-action_help_cb (GtkAction *action,
- ECompEditor *comp_editor)
+action_help_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ ECompEditor *self = user_data;
ECompEditorClass *klass;
- g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+ g_return_if_fail (E_IS_COMP_EDITOR (self));
- klass = E_COMP_EDITOR_GET_CLASS (comp_editor);
+ klass = E_COMP_EDITOR_GET_CLASS (self);
g_return_if_fail (klass->help_section != NULL);
- e_display_help (GTK_WINDOW (comp_editor), klass->help_section);
+ e_display_help (GTK_WINDOW (self), klass->help_section);
}
static void
@@ -1485,31 +1505,51 @@ ece_print_or_preview (ECompEditor *comp_editor,
}
static void
-action_print_cb (GtkAction *action,
- ECompEditor *comp_editor)
+action_print_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
- ece_print_or_preview (comp_editor, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG);
+ ECompEditor *self = user_data;
+
+ g_return_if_fail (E_IS_COMP_EDITOR (self));
+
+ ece_print_or_preview (self, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG);
}
static void
-action_print_preview_cb (GtkAction *action,
- ECompEditor *comp_editor)
+action_print_preview_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
- ece_print_or_preview (comp_editor, GTK_PRINT_OPERATION_ACTION_PREVIEW);
+ ECompEditor *self = user_data;
+
+ g_return_if_fail (E_IS_COMP_EDITOR (self));
+
+ ece_print_or_preview (self, GTK_PRINT_OPERATION_ACTION_PREVIEW);
}
static void
-action_save_cb (GtkAction *action,
- ECompEditor *comp_editor)
+action_save_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
- e_comp_editor_save_and_close (comp_editor, FALSE);
+ ECompEditor *self = user_data;
+
+ g_return_if_fail (E_IS_COMP_EDITOR (self));
+
+ e_comp_editor_save_and_close (self, FALSE);
}
static void
-action_save_and_close_cb (GtkAction *action,
- ECompEditor *comp_editor)
+action_save_and_close_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
- e_comp_editor_save_and_close (comp_editor, TRUE);
+ ECompEditor *self = user_data;
+
+ g_return_if_fail (E_IS_COMP_EDITOR (self));
+
+ e_comp_editor_save_and_close (self, TRUE);
}
static gboolean
@@ -1697,7 +1737,7 @@ static void
ece_sensitize_widgets (ECompEditor *comp_editor,
gboolean force_insensitive)
{
- GtkActionGroup *group;
+ EUIActionGroup *group;
GSList *link;
g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
@@ -1712,11 +1752,11 @@ ece_sensitize_widgets (ECompEditor *comp_editor,
e_comp_editor_page_sensitize_widgets (page, force_insensitive);
}
- group = e_comp_editor_get_action_group (comp_editor, "individual");
- gtk_action_group_set_sensitive (group, !force_insensitive);
+ group = ece_get_action_group (comp_editor, "individual");
+ e_ui_action_group_set_sensitive (group, !force_insensitive);
- group = e_comp_editor_get_action_group (comp_editor, "editable");
- gtk_action_group_set_sensitive (group, !force_insensitive);
+ group = ece_get_action_group (comp_editor, "editable");
+ e_ui_action_group_set_sensitive (group, !force_insensitive);
}
static void
@@ -1836,7 +1876,7 @@ comp_editor_delete_event (GtkWidget *widget,
/* It's disabled when the component is being saved */
if (gtk_widget_get_sensitive (GTK_WIDGET (comp_editor->priv->content)))
- action_close_cb (NULL, comp_editor);
+ action_close_cb (NULL, NULL, comp_editor);
return TRUE;
}
@@ -1853,10 +1893,10 @@ comp_editor_key_press_event (GtkWidget *widget,
if (event->keyval == GDK_KEY_Escape &&
!e_alert_bar_close_alert (comp_editor->priv->alert_bar)) {
- GtkAction *action;
+ EUIAction *action;
action = e_comp_editor_get_action (comp_editor, "close");
- gtk_action_activate (action);
+ g_action_activate (G_ACTION (action), NULL);
return TRUE;
}
@@ -1950,38 +1990,54 @@ e_comp_editor_set_shell (ECompEditor *comp_editor,
comp_editor->priv->shell = g_object_ref (shell);
}
-static GtkWidget *
-comp_editor_construct_header_bar (ECompEditor *comp_editor,
- GtkWidget *menu_button)
+static gboolean
+comp_editor_ui_manager_create_item_cb (EUIManager *manager,
+ EUIElement *elem,
+ EUIAction *action,
+ EUIElementKind for_kind,
+ GObject **out_item,
+ gpointer user_data)
{
- GtkWidget *widget;
- GtkWidget *button;
- GtkAction *action;
- GtkHeaderBar *header_bar;
+ ECompEditor *self = user_data;
- widget = gtk_header_bar_new ();
- gtk_widget_show (widget);
- header_bar = GTK_HEADER_BAR (widget);
- gtk_header_bar_set_show_close_button (header_bar, TRUE);
+ g_return_val_if_fail (E_IS_COMP_EDITOR (self), FALSE);
- if (menu_button)
- gtk_header_bar_pack_end (header_bar, menu_button);
+ if (for_kind != E_UI_ELEMENT_KIND_HEADERBAR ||
+ g_strcmp0 (g_action_get_name (G_ACTION (action)), "menu-button") != 0)
+ return FALSE;
- action = e_comp_editor_get_action (comp_editor, "save-and-close");
+ if (self->priv->menu_button)
+ *out_item = G_OBJECT (g_object_ref (self->priv->menu_button));
+ else
+ *out_item = NULL;
- button = e_header_bar_button_new (_("Save and Close"), action);
- e_header_bar_button_css_add_class (E_HEADER_BAR_BUTTON (button), "suggested-action");
- e_header_bar_button_set_show_icon_only (E_HEADER_BAR_BUTTON (button), FALSE);
- gtk_widget_show (button);
- gtk_header_bar_pack_start (header_bar, button);
+ return TRUE;
+}
- action = e_comp_editor_get_action (comp_editor, "save");
+static gboolean
+comp_editor_ui_manager_create_gicon_cb (EUIManager *manager,
+ const gchar *name,
+ GIcon **out_gicon)
+{
+ GIcon *icon;
+ GIcon *emblemed_icon;
+ GEmblem *emblem;
- button = e_header_bar_button_new (NULL, action);
- gtk_widget_show (button);
- gtk_header_bar_pack_start (header_bar, button);
+ if (g_strcmp0 (name, "ECompEditor::save-and-close") != 0)
+ return FALSE;
- return widget;
+ icon = g_themed_icon_new ("window-close");
+ emblemed_icon = g_themed_icon_new ("document-save");
+ emblem = g_emblem_new (emblemed_icon);
+ g_object_unref (emblemed_icon);
+
+ emblemed_icon = g_emblemed_icon_new (icon, emblem);
+ g_object_unref (emblem);
+ g_object_unref (icon);
+
+ *out_gicon = emblemed_icon;
+
+ return TRUE;
}
static void
@@ -2139,137 +2195,91 @@ e_comp_editor_get_property (GObject *object,
static void
e_comp_editor_constructed (GObject *object)
{
- const gchar *ui =
- ""
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- "";
-
- GtkActionEntry core_entries[] = {
+ static const EUIActionEntry core_entries[] = {
{ "close",
"window-close",
N_("_Close"),
"w",
N_("Close the current window"),
- G_CALLBACK (action_close_cb) },
+ action_close_cb, NULL, NULL, NULL },
{ "copy-clipboard",
"edit-copy",
N_("_Copy"),
"c",
N_("Copy the selection"),
- NULL }, /* Handled by EFocusTracker */
+ NULL, NULL, NULL, NULL }, /* Handled by EFocusTracker */
{ "cut-clipboard",
"edit-cut",
N_("Cu_t"),
"x",
N_("Cut the selection"),
- NULL }, /* Handled by EFocusTracker */
+ NULL, NULL, NULL, NULL }, /* Handled by EFocusTracker */
{ "delete-selection",
"edit-delete",
N_("_Delete"),
NULL,
N_("Delete the selection"),
- NULL }, /* Handled by EFocusTracker */
+ NULL, NULL, NULL, NULL }, /* Handled by EFocusTracker */
{ "help",
"help-browser",
N_("_Help"),
"F1",
N_("View help"),
- G_CALLBACK (action_help_cb) },
+ action_help_cb, NULL, NULL, NULL },
{ "paste-clipboard",
"edit-paste",
N_("_Paste"),
"v",
N_("Paste the clipboard"),
- NULL }, /* Handled by EFocusTracker */
+ NULL, NULL, NULL, NULL }, /* Handled by EFocusTracker */
{ "print",
"document-print",
N_("_Print…"),
"p",
NULL,
- G_CALLBACK (action_print_cb) },
+ action_print_cb, NULL, NULL, NULL },
{ "print-preview",
"document-print-preview",
N_("Pre_view…"),
NULL,
NULL,
- G_CALLBACK (action_print_preview_cb) },
+ action_print_preview_cb, NULL, NULL, NULL },
{ "select-all",
"edit-select-all",
N_("Select _All"),
"a",
N_("Select all text"),
- NULL }, /* Handled by EFocusTracker */
+ NULL, NULL, NULL, NULL }, /* Handled by EFocusTracker */
+
+ { "show-toolbar",
+ NULL,
+ N_("Show _toolbar"),
+ NULL,
+ NULL,
+ NULL, NULL, "true", (EUIActionFunc) e_ui_action_set_state },
{ "undo",
"edit-undo",
N_("_Undo"),
"z",
N_("Undo"),
- NULL }, /* Handled by EFocusTracker */
+ NULL, NULL, NULL, NULL }, /* Handled by EFocusTracker */
{ "redo",
"edit-redo",
N_("_Redo"),
"y",
N_("Redo"),
- NULL }, /* Handled by EFocusTracker */
+ NULL, NULL, NULL, NULL }, /* Handled by EFocusTracker */
/* Menus */
@@ -2278,85 +2288,82 @@ e_comp_editor_constructed (GObject *object)
N_("_Classification"),
NULL,
NULL,
- NULL },
+ NULL, NULL, NULL, NULL },
{ "edit-menu",
NULL,
N_("_Edit"),
NULL,
NULL,
- NULL },
+ NULL, NULL, NULL, NULL },
{ "file-menu",
NULL,
N_("_File"),
NULL,
NULL,
- NULL },
+ NULL, NULL, NULL, NULL },
{ "help-menu",
NULL,
N_("_Help"),
NULL,
NULL,
- NULL },
+ NULL, NULL, NULL, NULL },
{ "insert-menu",
NULL,
N_("_Insert"),
NULL,
NULL,
- NULL },
+ NULL, NULL, NULL, NULL },
+
+ { "menu-button",
+ NULL,
+ N_("Menu"),
+ NULL,
+ NULL,
+ NULL, NULL, NULL, NULL },
{ "options-menu",
NULL,
N_("_Options"),
NULL,
NULL,
- NULL },
+ NULL, NULL, NULL, NULL },
{ "view-menu",
NULL,
N_("_View"),
NULL,
NULL,
- NULL }
+ NULL, NULL, NULL, NULL }
};
- GtkActionEntry editable_entries[] = {
+ static const EUIActionEntry editable_entries[] = {
{ "save",
"document-save",
N_("_Save"),
"s",
N_("Save current changes"),
- G_CALLBACK (action_save_cb) },
+ action_save_cb, NULL, NULL, NULL },
{ "save-and-close",
- NULL,
+ "gicon::ECompEditor::save-and-close",
N_("Save and Close"),
"Return",
N_("Save current changes and close editor"),
- G_CALLBACK (action_save_and_close_cb) }
- };
-
- GtkToggleActionEntry toggle_entries[] = {
- { "show-toolbar",
- NULL,
- N_("Show _toolbar"),
- NULL,
- NULL,
- NULL },
+ action_save_and_close_cb, NULL, NULL, NULL }
};
ECompEditor *comp_editor = E_COMP_EDITOR (object);
+ GObject *ui_item;
GtkWidget *widget;
- GtkWidget *menu_button = NULL;
GtkBox *vbox;
- GtkAction *action;
- GtkActionGroup *action_group;
+ EUIAction *action;
EFocusTracker *focus_tracker;
- GError *error = NULL;
+ GError *local_error = NULL;
G_OBJECT_CLASS (e_comp_editor_parent_class)->constructed (object);
@@ -2364,89 +2371,51 @@ e_comp_editor_constructed (GObject *object)
G_CALLBACK (e_util_check_gtk_bindings_in_key_press_event_cb), NULL);
comp_editor->priv->calendar_settings = e_util_ref_settings ("org.gnome.evolution.calendar");
- comp_editor->priv->ui_manager = gtk_ui_manager_new ();
+ comp_editor->priv->ui_manager = e_ui_manager_new ();
+
+ g_signal_connect (comp_editor->priv->ui_manager, "create-item",
+ G_CALLBACK (comp_editor_ui_manager_create_item_cb), comp_editor);
+ g_signal_connect (comp_editor->priv->ui_manager, "create-gicon",
+ G_CALLBACK (comp_editor_ui_manager_create_gicon_cb), comp_editor);
gtk_window_add_accel_group (
GTK_WINDOW (comp_editor),
- gtk_ui_manager_get_accel_group (comp_editor->priv->ui_manager));
+ e_ui_manager_get_accel_group (comp_editor->priv->ui_manager));
/* Setup Action Groups */
- action_group = gtk_action_group_new ("individual");
- gtk_action_group_set_translation_domain (
- action_group, GETTEXT_PACKAGE);
- gtk_ui_manager_insert_action_group (
- comp_editor->priv->ui_manager, action_group, 0);
- g_object_unref (action_group);
+ e_ui_manager_add_actions (comp_editor->priv->ui_manager, "core", GETTEXT_PACKAGE,
+ core_entries, G_N_ELEMENTS (core_entries), comp_editor);
- action_group = gtk_action_group_new ("core");
- gtk_action_group_set_translation_domain (
- action_group, GETTEXT_PACKAGE);
- gtk_action_group_add_actions (
- action_group, core_entries,
- G_N_ELEMENTS (core_entries), comp_editor);
- gtk_ui_manager_insert_action_group (
- comp_editor->priv->ui_manager, action_group, 0);
- g_object_unref (action_group);
+ e_ui_manager_add_actions (comp_editor->priv->ui_manager, "editable", GETTEXT_PACKAGE,
+ editable_entries, G_N_ELEMENTS (editable_entries), comp_editor);
- action_group = gtk_action_group_new ("editable");
- gtk_action_group_set_translation_domain (
- action_group, GETTEXT_PACKAGE);
- gtk_action_group_add_actions (
- action_group, editable_entries,
- G_N_ELEMENTS (editable_entries), comp_editor);
- gtk_ui_manager_insert_action_group (
- comp_editor->priv->ui_manager, action_group, 0);
- g_object_unref (action_group);
+ e_ui_manager_set_action_groups_widget (comp_editor->priv->ui_manager, GTK_WIDGET (comp_editor));
- action = gtk_action_group_get_action (action_group, "save-and-close");
+ action = e_comp_editor_get_action (comp_editor, "save-and-close");
if (action) {
- GtkAction *save_action;
- GIcon *icon;
- GIcon *emblemed_icon;
- GEmblem *emblem;
+ EUIAction *save_action;
- icon = g_themed_icon_new ("window-close");
- emblemed_icon = g_themed_icon_new ("document-save");
- emblem = g_emblem_new (emblemed_icon);
- g_object_unref (emblemed_icon);
+ save_action = e_comp_editor_get_action (comp_editor, "save");
- emblemed_icon = g_emblemed_icon_new (icon, emblem);
- g_object_unref (emblem);
- g_object_unref (icon);
-
- gtk_action_set_gicon (action, emblemed_icon);
-
- g_object_unref (emblemed_icon);
-
- save_action = gtk_action_group_get_action (action_group, "save");
e_binding_bind_property (
save_action, "sensitive",
action, "sensitive",
G_BINDING_SYNC_CREATE);
}
- action_group = gtk_action_group_new ("toggle");
- gtk_action_group_add_toggle_actions (
- action_group, toggle_entries,
- G_N_ELEMENTS (toggle_entries), comp_editor);
- gtk_ui_manager_insert_action_group (
- comp_editor->priv->ui_manager, action_group, 0);
- g_object_unref (action_group);
-
- action = gtk_action_group_get_action (action_group, "show-toolbar");
+ action = e_comp_editor_get_action (comp_editor, "show-toolbar");
if (action) {
g_settings_bind (
- comp_editor->priv->calendar_settings, "editor-show-toolbar",
- action, "active",
- G_SETTINGS_BIND_DEFAULT);
+ comp_editor->priv->calendar_settings, "editor-show-toolbar",
+ action, "active",
+ G_SETTINGS_BIND_DEFAULT);
}
- gtk_ui_manager_add_ui_from_string (comp_editor->priv->ui_manager, ui, -1, &error);
- if (error != NULL) {
- g_warning ("%s: %s", G_STRFUNC, error->message);
- g_error_free (error);
- }
+ if (!e_ui_parser_merge_file (e_ui_manager_get_parser (comp_editor->priv->ui_manager), "e-comp-editor.eui", &local_error))
+ g_warning ("%s: Failed to read e-comp-editor.eui file: %s", G_STRFUNC, local_error ? local_error->message : "Unknown error");
+
+ g_clear_error (&local_error);
widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
g_object_set (G_OBJECT (widget),
@@ -2462,34 +2431,25 @@ e_comp_editor_constructed (GObject *object)
gtk_container_add (GTK_CONTAINER (comp_editor), widget);
/* Construct the main menu and headerbar. */
- widget = e_comp_editor_get_managed_widget (comp_editor, "/main-menu");
- comp_editor->priv->menu_bar = e_menu_bar_new (GTK_MENU_BAR (widget), GTK_WINDOW (comp_editor), &menu_button);
+ ui_item = e_ui_manager_create_item (comp_editor->priv->ui_manager, "main-menu");
+ widget = gtk_menu_bar_new_from_model (G_MENU_MODEL (ui_item));
+ g_clear_object (&ui_item);
+
+ comp_editor->priv->menu_bar = e_menu_bar_new (GTK_MENU_BAR (widget), GTK_WINDOW (comp_editor), &comp_editor->priv->menu_button);
gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
if (e_util_get_use_header_bar ()) {
- widget = comp_editor_construct_header_bar (comp_editor, menu_button);
+ ui_item = e_ui_manager_create_item (comp_editor->priv->ui_manager, "main-headerbar");
+ widget = GTK_WIDGET (ui_item);
gtk_window_set_titlebar (GTK_WINDOW (comp_editor), widget);
- /* Destroy items from the toolbar, which are in the header bar */
- widget = e_comp_editor_get_managed_widget (comp_editor, "/main-toolbar/save-and-close");
- gtk_widget_destroy (widget);
-
- widget = e_comp_editor_get_managed_widget (comp_editor, "/main-toolbar/save");
- gtk_widget_destroy (widget);
- } else if (menu_button) {
- g_object_ref_sink (menu_button);
- gtk_widget_destroy (menu_button);
+ ui_item = e_ui_manager_create_item (comp_editor->priv->ui_manager, "toolbar-with-headerbar");
+ } else {
+ ui_item = e_ui_manager_create_item (comp_editor->priv->ui_manager, "toolbar-without-headerbar");
}
- widget = e_comp_editor_get_managed_widget (comp_editor, "/main-toolbar");
+ widget = GTK_WIDGET (ui_item);
gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
- gtk_widget_show (widget);
-
- e_util_setup_toolbar_icon_size (GTK_TOOLBAR (widget), GTK_ICON_SIZE_BUTTON);
-
- gtk_style_context_add_class (
- gtk_widget_get_style_context (widget),
- GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
g_settings_bind (
comp_editor->priv->calendar_settings, "editor-show-toolbar",
@@ -2564,7 +2524,7 @@ e_comp_editor_constructed (GObject *object)
/* Desensitize the "save" action. */
action = e_comp_editor_get_action (comp_editor, "save");
- gtk_action_set_sensitive (action, FALSE);
+ e_ui_action_set_sensitive (action, FALSE);
e_binding_bind_property (comp_editor, "changed", action, "sensitive", 0);
@@ -2619,6 +2579,7 @@ e_comp_editor_dispose (GObject *object)
g_clear_object (&comp_editor->priv->validation_alert);
g_clear_object (&comp_editor->priv->menu_bar);
+ comp_editor->priv->menu_button = NULL;
comp_editor->priv->activity_bar = NULL;
opened_editors = g_slist_remove (opened_editors, comp_editor);
@@ -3075,7 +3036,7 @@ e_comp_editor_get_focus_tracker (ECompEditor *comp_editor)
return comp_editor->priv->focus_tracker;
}
-GtkUIManager *
+EUIManager *
e_comp_editor_get_ui_manager (ECompEditor *comp_editor)
{
g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
@@ -3083,49 +3044,18 @@ e_comp_editor_get_ui_manager (ECompEditor *comp_editor)
return comp_editor->priv->ui_manager;
}
-GtkAction *
+EUIAction *
e_comp_editor_get_action (ECompEditor *comp_editor,
const gchar *action_name)
{
- GtkUIManager *ui_manager;
+ EUIManager *ui_manager;
g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
g_return_val_if_fail (action_name != NULL, NULL);
ui_manager = e_comp_editor_get_ui_manager (comp_editor);
- return e_lookup_action (ui_manager, action_name);
-}
-
-GtkActionGroup *
-e_comp_editor_get_action_group (ECompEditor *comp_editor,
- const gchar *group_name)
-{
- GtkUIManager *ui_manager;
-
- g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
- g_return_val_if_fail (group_name != NULL, NULL);
-
- ui_manager = e_comp_editor_get_ui_manager (comp_editor);
-
- return e_lookup_action_group (ui_manager, group_name);
-}
-
-GtkWidget *
-e_comp_editor_get_managed_widget (ECompEditor *comp_editor,
- const gchar *widget_path)
-{
- GtkUIManager *ui_manager;
- GtkWidget *widget;
-
- g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
- g_return_val_if_fail (widget_path != NULL, NULL);
-
- ui_manager = e_comp_editor_get_ui_manager (comp_editor);
- widget = gtk_ui_manager_get_widget (ui_manager, widget_path);
- g_return_val_if_fail (widget != NULL, NULL);
-
- return widget;
+ return e_ui_manager_get_action (ui_manager, action_name);
}
static gchar *
diff --git a/src/calendar/gui/e-comp-editor.h b/src/calendar/gui/e-comp-editor.h
index e8b43c965d..f9f86c223c 100644
--- a/src/calendar/gui/e-comp-editor.h
+++ b/src/calendar/gui/e-comp-editor.h
@@ -109,14 +109,9 @@ guint32 e_comp_editor_get_flags (ECompEditor *comp_editor);
void e_comp_editor_set_flags (ECompEditor *comp_editor,
guint32 flags);
EFocusTracker * e_comp_editor_get_focus_tracker (ECompEditor *comp_editor);
-GtkUIManager * e_comp_editor_get_ui_manager (ECompEditor *comp_editor);
-GtkAction * e_comp_editor_get_action (ECompEditor *comp_editor,
+EUIManager * e_comp_editor_get_ui_manager (ECompEditor *comp_editor);
+EUIAction * e_comp_editor_get_action (ECompEditor *comp_editor,
const gchar *action_name);
-GtkActionGroup *e_comp_editor_get_action_group (ECompEditor *comp_editor,
- const gchar *group_name);
-GtkWidget * e_comp_editor_get_managed_widget
- (ECompEditor *comp_editor,
- const gchar *widget_path);
const gchar * e_comp_editor_get_alarm_email_address
(ECompEditor *comp_editor);
void e_comp_editor_set_alarm_email_address
diff --git a/src/calendar/gui/e-memo-table.c b/src/calendar/gui/e-memo-table.c
index 829f5bf379..ac482553bd 100644
--- a/src/calendar/gui/e-memo-table.c
+++ b/src/calendar/gui/e-memo-table.c
@@ -628,7 +628,7 @@ memo_table_update_actions (ESelectable *selectable,
gint n_clipboard_targets)
{
EMemoTable *memo_table;
- GtkAction *action;
+ EUIAction *action;
GtkTargetList *target_list;
GSList *list, *iter;
gboolean can_paste = FALSE;
@@ -660,32 +660,32 @@ memo_table_update_actions (ESelectable *selectable,
action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
sensitive = (n_selected > 0) && sources_are_editable && !is_editing;
tooltip = _("Cut selected memos to the clipboard");
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, tooltip);
action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
sensitive = (n_selected > 0) && !is_editing;
tooltip = _("Copy selected memos to the clipboard");
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, tooltip);
action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
sensitive = sources_are_editable && can_paste && !is_editing;
tooltip = _("Paste memos from the clipboard");
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, tooltip);
action = e_focus_tracker_get_delete_selection_action (focus_tracker);
sensitive = (n_selected > 0) && sources_are_editable && !is_editing;
tooltip = _("Delete selected memos");
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, tooltip);
action = e_focus_tracker_get_select_all_action (focus_tracker);
sensitive = TRUE;
tooltip = _("Select all visible memos");
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, tooltip);
}
static void
diff --git a/src/calendar/gui/e-task-table.c b/src/calendar/gui/e-task-table.c
index 52c52e97f5..64eabe3fd4 100644
--- a/src/calendar/gui/e-task-table.c
+++ b/src/calendar/gui/e-task-table.c
@@ -808,7 +808,7 @@ task_table_update_actions (ESelectable *selectable,
gint n_clipboard_targets)
{
ETaskTable *task_table;
- GtkAction *action;
+ EUIAction *action;
GtkTargetList *target_list;
GSList *list, *iter;
gboolean can_paste = FALSE;
@@ -843,32 +843,32 @@ task_table_update_actions (ESelectable *selectable,
action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
sensitive = (n_selected > 0) && sources_are_editable && !is_editing;
tooltip = _("Cut selected tasks to the clipboard");
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, tooltip);
action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
sensitive = (n_selected > 0) && !is_editing;
tooltip = _("Copy selected tasks to the clipboard");
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, tooltip);
action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
sensitive = sources_are_editable && can_paste && !is_editing;
tooltip = _("Paste tasks from the clipboard");
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, tooltip);
action = e_focus_tracker_get_delete_selection_action (focus_tracker);
sensitive = (n_selected > 0) && sources_are_editable && !is_editing;
tooltip = _("Delete selected tasks");
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, tooltip);
action = e_focus_tracker_get_select_all_action (focus_tracker);
sensitive = TRUE;
tooltip = _("Select all visible tasks");
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, tooltip);
}
static void
diff --git a/src/composer/CMakeLists.txt b/src/composer/CMakeLists.txt
index b609d8dbe5..eae1e94fdd 100644
--- a/src/composer/CMakeLists.txt
+++ b/src/composer/CMakeLists.txt
@@ -1,9 +1,5 @@
add_error_files(composer mail-composer.error)
-install(FILES evolution-composer.ui
- DESTINATION ${uidir}
-)
-
set(DEPENDENCIES
econtacteditor
econtactlisteditor
diff --git a/src/composer/e-composer-actions.c b/src/composer/e-composer-actions.c
index 8d026c8546..3fae83eae4 100644
--- a/src/composer/e-composer-actions.c
+++ b/src/composer/e-composer-actions.c
@@ -26,12 +26,16 @@
#include
static void
-action_attach_cb (GtkAction *action,
- EMsgComposer *composer)
+action_attach_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EMsgComposer *composer = user_data;
EAttachmentView *view;
EAttachmentStore *store;
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+
view = e_msg_composer_get_attachment_view (composer);
store = e_attachment_view_get_store (view);
@@ -39,25 +43,29 @@ action_attach_cb (GtkAction *action,
}
static void
-action_charset_cb (GtkRadioAction *action,
- GtkRadioAction *current,
- EMsgComposer *composer)
+action_charset_change_set_state_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
- const gchar *charset;
+ EMsgComposer *composer = user_data;
- if (action != current)
- return;
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
- charset = g_object_get_data (G_OBJECT (action), "charset");
+ e_ui_action_set_state (action, parameter);
g_free (composer->priv->charset);
- composer->priv->charset = g_strdup (charset);
+ composer->priv->charset = g_strdup (g_variant_get_string (parameter, NULL));
}
static void
-action_close_cb (GtkAction *action,
- EMsgComposer *composer)
+action_close_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EMsgComposer *composer = user_data;
+
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+
if (e_msg_composer_can_close (composer, TRUE)) {
e_composer_emit_before_destroy (composer);
gtk_widget_destroy (GTK_WIDGET (composer));
@@ -82,11 +90,15 @@ action_new_message_composer_created_cb (GObject *source_object,
}
static void
-action_new_message_cb (GtkAction *action,
- EMsgComposer *composer)
+action_new_message_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EMsgComposer *composer = user_data;
EShell *shell;
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+
shell = e_msg_composer_get_shell (composer);
e_msg_composer_new (shell, action_new_message_composer_created_cb, NULL);
@@ -105,27 +117,17 @@ composer_set_content_editor_changed (EMsgComposer *composer)
}
static void
-action_pgp_encrypt_cb (GtkToggleAction *action,
- EMsgComposer *composer)
-{
- composer_set_content_editor_changed (composer);
-}
-
-static void
-action_pgp_sign_cb (GtkToggleAction *action,
- EMsgComposer *composer)
-{
- composer_set_content_editor_changed (composer);
-}
-
-static void
-action_preferences_cb (GtkAction *action,
- EMsgComposer *composer)
+action_preferences_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EMsgComposer *composer = user_data;
EShell *shell;
GtkWidget *preferences_window;
const gchar *page_name = "composer";
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+
shell = e_msg_composer_get_shell (composer);
preferences_window = e_shell_get_preferences_window (shell);
e_preferences_window_setup (E_PREFERENCES_WINDOW (preferences_window));
@@ -143,21 +145,29 @@ action_preferences_cb (GtkAction *action,
}
static void
-action_print_cb (GtkAction *action,
- EMsgComposer *composer)
+action_print_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EMsgComposer *composer = user_data;
GtkPrintOperationAction print_action;
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+
print_action = GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG;
e_msg_composer_print (composer, print_action);
}
static void
-action_print_preview_cb (GtkAction *action,
- EMsgComposer *composer)
+action_print_preview_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EMsgComposer *composer = user_data;
GtkPrintOperationAction print_action;
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+
print_action = GTK_PRINT_OPERATION_ACTION_PREVIEW;
e_msg_composer_print (composer, print_action);
}
@@ -187,17 +197,21 @@ action_save_ready_cb (GObject *source_object,
}
static void
-action_save_cb (GtkAction *action,
- EMsgComposer *composer)
+action_save_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EMsgComposer *composer = user_data;
EHTMLEditor *editor;
const gchar *filename;
gint fd;
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+
editor = e_msg_composer_get_editor (composer);
filename = e_html_editor_get_filename (editor);
if (filename == NULL) {
- gtk_action_activate (ACTION (SAVE_AS));
+ g_action_activate (G_ACTION (ACTION (SAVE_AS)), NULL);
return;
}
@@ -229,13 +243,17 @@ action_save_cb (GtkAction *action,
}
static void
-action_save_as_cb (GtkAction *action,
- EMsgComposer *composer)
+action_save_as_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EMsgComposer *composer = user_data;
EHTMLEditor *editor;
GtkFileChooserNative *native;
gchar *filename;
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+
native = gtk_file_chooser_native_new (
_("Save as…"), GTK_WINDOW (composer),
GTK_FILE_CHOOSER_ACTION_SAVE,
@@ -254,52 +272,58 @@ action_save_as_cb (GtkAction *action,
e_html_editor_set_filename (editor, filename);
g_free (filename);
- gtk_action_activate (ACTION (SAVE));
+ g_action_activate (G_ACTION (ACTION (SAVE)), NULL);
}
g_object_unref (native);
}
static void
-action_save_draft_cb (GtkAction *action,
- EMsgComposer *composer)
+action_save_draft_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EMsgComposer *composer = user_data;
+
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+
e_msg_composer_save_to_drafts (composer);
}
static void
-action_send_cb (GtkAction *action,
- EMsgComposer *composer)
+action_send_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EMsgComposer *composer = user_data;
+
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+
e_msg_composer_send (composer);
}
static void
-action_smime_encrypt_cb (GtkToggleAction *action,
- EMsgComposer *composer)
+e_composer_set_action_state_with_changed_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EMsgComposer *composer = user_data;
+
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+
+ e_ui_action_set_state (action, parameter);
composer_set_content_editor_changed (composer);
}
static void
-action_smime_sign_cb (GtkToggleAction *action,
- EMsgComposer *composer)
+composer_actions_toolbar_option_notify_active_cb (EUIAction *action,
+ GParamSpec *param,
+ gpointer user_data)
{
- composer_set_content_editor_changed (composer);
-}
-
-static void
-composer_actions_toolbar_option_toggled_cb (GtkToggleAction *toggle_action,
- EMsgComposer *composer)
-{
- GtkAction *action;
-
- action = GTK_ACTION (toggle_action);
-
/* Show the action only after the first time the option is used */
- if (!gtk_action_get_visible (action) &&
- gtk_toggle_action_get_active (toggle_action))
- gtk_action_set_visible (action, TRUE);
+ if (!e_ui_action_get_visible (action) &&
+ e_ui_action_get_active (action))
+ e_ui_action_set_visible (action, TRUE);
}
static gboolean
@@ -319,285 +343,6 @@ composer_actions_accel_activate_cb (GtkAccelGroup *accel_group,
return FALSE;
}
-static GtkActionEntry entries[] = {
-
- { "attach",
- "mail-attachment",
- N_("_Attachment…"),
- "m",
- N_("Attach a file"),
- G_CALLBACK (action_attach_cb) },
-
- { "close",
- "window-close",
- N_("_Close"),
- "w",
- N_("Close the current file"),
- G_CALLBACK (action_close_cb) },
-
- { "new-message",
- "mail-message-new",
- N_("New _Message"),
- "n",
- N_("Open New Message window"),
- G_CALLBACK (action_new_message_cb) },
-
- { "preferences",
- "preferences-system",
- N_("_Preferences"),
- NULL,
- N_("Configure Evolution"),
- G_CALLBACK (action_preferences_cb) },
-
- { "save",
- "document-save",
- N_("_Save"),
- "s",
- N_("Save the current file"),
- G_CALLBACK (action_save_cb) },
-
- { "save-as",
- "document-save-as",
- N_("Save _As…"),
- NULL,
- N_("Save the current file with a different name"),
- G_CALLBACK (action_save_as_cb) },
-
- /* Menus */
-
- { "charset-menu",
- NULL,
- N_("Character _Encoding"),
- NULL,
- NULL,
- NULL },
-
- { "options-menu",
- NULL,
- N_("Option_s"),
- NULL,
- NULL,
- NULL }
-};
-
-static GtkActionEntry async_entries[] = {
-
- { "print",
- "document-print",
- N_("_Print…"),
- "p",
- NULL,
- G_CALLBACK (action_print_cb) },
-
- { "print-preview",
- "document-print-preview",
- N_("Print Pre_view"),
- "p",
- NULL,
- G_CALLBACK (action_print_preview_cb) },
-
- { "save-draft",
- "document-save",
- N_("Save as _Draft"),
- "s",
- N_("Save as draft"),
- G_CALLBACK (action_save_draft_cb) },
-
- { "send",
- "mail-send",
- N_("S_end"),
- "Return",
- N_("Send this message"),
- G_CALLBACK (action_send_cb) },
-};
-
-static GtkToggleActionEntry toggle_entries[] = {
-
- { "toolbar-show-main",
- NULL,
- N_("_Main toolbar"),
- NULL,
- N_("Main toolbar"),
- NULL,
- FALSE },
-
- { "toolbar-show-edit",
- NULL,
- N_("Edit _toolbar"),
- NULL,
- N_("Edit toolbar"),
- NULL,
- FALSE },
-
- { "pgp-encrypt",
- NULL,
- N_("PGP _Encrypt"),
- NULL,
- N_("Encrypt this message with PGP"),
- G_CALLBACK (action_pgp_encrypt_cb),
- FALSE },
-
- { "pgp-sign",
- NULL,
- N_("PGP _Sign"),
- NULL,
- N_("Sign this message with your PGP key"),
- G_CALLBACK (action_pgp_sign_cb),
- FALSE },
-
- { "picture-gallery",
- "emblem-photos",
- N_("_Picture Gallery"),
- NULL,
- N_("Show a collection of pictures that you can drag to your message"),
- NULL, /* no callback */
- FALSE },
-
- { "prioritize-message",
- "mail-mark-important",
- N_("_Prioritize Message"),
- NULL,
- N_("Set the message priority to high"),
- NULL, /* no callback */
- FALSE },
-
- { "request-read-receipt",
- "preferences-system-notifications",
- N_("Re_quest Read Receipt"),
- NULL,
- N_("Get delivery notification when your message is read"),
- NULL, /* no callback */
- FALSE },
-
- { "delivery-status-notification",
- NULL,
- N_("Request _Delivery Status Notification"),
- NULL,
- N_("Get delivery status notification for the message"),
- NULL, /* no callback */
- FALSE },
-
- { "smime-encrypt",
- NULL,
- N_("S/MIME En_crypt"),
- NULL,
- N_("Encrypt this message with S/MIME"),
- G_CALLBACK (action_smime_encrypt_cb),
- FALSE },
-
- { "smime-sign",
- NULL,
- N_("S/MIME Sig_n"),
- NULL,
- N_("Sign this message with your S/MIME Signature Certificate"),
- G_CALLBACK (action_smime_sign_cb),
- FALSE },
-
- { "toolbar-pgp-encrypt",
- "security-high",
- NULL,
- NULL,
- NULL,
- NULL,
- FALSE },
-
- { "toolbar-pgp-sign",
- "stock_signature",
- NULL,
- NULL,
- NULL,
- NULL,
- FALSE },
-
- { "toolbar-prioritize-message",
- "emblem-important",
- NULL,
- NULL,
- NULL,
- NULL,
- FALSE },
-
- { "toolbar-request-read-receipt",
- "mail-forward",
- NULL,
- NULL,
- NULL,
- NULL,
- FALSE },
-
- { "toolbar-smime-encrypt",
- "security-high",
- NULL,
- NULL,
- NULL,
- NULL,
- FALSE },
-
- { "toolbar-smime-sign",
- "stock_signature",
- NULL,
- NULL,
- NULL,
- NULL,
- FALSE },
-
- { "view-bcc",
- NULL,
- N_("_Bcc Field"),
- NULL,
- N_("Toggles whether the BCC field is displayed"),
- NULL, /* Handled by property bindings */
- FALSE },
-
- { "view-cc",
- NULL,
- N_("_Cc Field"),
- NULL,
- N_("Toggles whether the CC field is displayed"),
- NULL, /* Handled by property bindings */
- FALSE },
-
- { "view-from-override",
- NULL,
- N_("_From Override Field"),
- NULL,
- N_("Toggles whether the From override field to change name or email address is displayed"),
- NULL, /* Handled by property bindings */
- FALSE },
-
- { "view-mail-followup-to",
- NULL,
- N_("Mail-Follow_up-To Field"),
- NULL,
- N_("Toggles whether the Mail-Followup-To field is displayed"),
- NULL, /* Handled by property bindings */
- FALSE },
-
- { "view-mail-reply-to",
- NULL,
- N_("Mail-R_eply-To Field"),
- NULL,
- N_("Toggles whether the Mail-Reply-To field is displayed"),
- NULL, /* Handled by property bindings */
- FALSE },
-
- { "view-reply-to",
- NULL,
- N_("_Reply-To Field"),
- NULL,
- N_("Toggles whether the Reply-To field is displayed"),
- NULL, /* Handled by property bindings */
- FALSE },
-
- { "visually-wrap-long-lines",
- NULL,
- N_("Visually _Wrap Long Lines"),
- NULL,
- N_("Whether to visually wrap long lines of text to avoid horizontal scrolling"),
- NULL,
- FALSE }
-};
-
static gboolean
eca_transform_mode_html_to_boolean_cb (GBinding *binding,
const GValue *source_value,
@@ -630,15 +375,276 @@ eca_mode_to_bool_hide_in_markdown_cb (GBinding *binding,
void
e_composer_actions_init (EMsgComposer *composer)
{
- GtkActionGroup *action_group;
+ static const EUIActionEntry entries[] = {
+
+ { "attach",
+ "mail-attachment",
+ N_("_Attachment…"),
+ "m",
+ N_("Attach a file"),
+ action_attach_cb, NULL, NULL, NULL },
+
+ { "close",
+ "window-close",
+ N_("_Close"),
+ "w",
+ N_("Close the current file"),
+ action_close_cb, NULL, NULL, NULL },
+
+ { "new-message",
+ "mail-message-new",
+ N_("New _Message"),
+ "n",
+ N_("Open New Message window"),
+ action_new_message_cb, NULL, NULL, NULL },
+
+ { "preferences",
+ "preferences-system",
+ N_("_Preferences"),
+ NULL,
+ N_("Configure Evolution"),
+ action_preferences_cb, NULL, NULL, NULL },
+
+ { "save",
+ "document-save",
+ N_("_Save"),
+ "s",
+ N_("Save the current file"),
+ action_save_cb, NULL, NULL, NULL },
+
+ { "save-as",
+ "document-save-as",
+ N_("Save _As…"),
+ NULL,
+ N_("Save the current file with a different name"),
+ action_save_as_cb, NULL, NULL, NULL },
+
+ /* Menus */
+
+ { "EMsgComposer::charset-menu",
+ NULL,
+ N_("Character _Encoding"),
+ NULL,
+ NULL,
+ NULL, "s", "''", action_charset_change_set_state_cb },
+
+ { "EMsgComposer::menu-button",
+ NULL,
+ N_("Menu"),
+ NULL,
+ NULL,
+ NULL, NULL, NULL, NULL },
+
+ { "options-menu",
+ NULL,
+ N_("Option_s"),
+ NULL,
+ NULL,
+ NULL, NULL, NULL, NULL }
+ };
+
+ static const EUIActionEntry async_entries[] = {
+
+ { "print",
+ "document-print",
+ N_("_Print…"),
+ "p",
+ NULL,
+ action_print_cb, NULL, NULL, NULL },
+
+ { "print-preview",
+ "document-print-preview",
+ N_("Print Pre_view"),
+ "p",
+ NULL,
+ action_print_preview_cb, NULL, NULL, NULL },
+
+ { "save-draft",
+ "document-save",
+ N_("Save as _Draft"),
+ "s",
+ N_("Save as draft"),
+ action_save_draft_cb, NULL, NULL, NULL },
+
+ { "send",
+ "mail-send",
+ N_("S_end"),
+ "Return",
+ N_("Send this message"),
+ action_send_cb, NULL, NULL, NULL },
+ };
+
+ static const EUIActionEntry toggle_entries[] = {
+
+ { "toolbar-show-main",
+ NULL,
+ N_("_Main toolbar"),
+ NULL,
+ N_("Main toolbar"),
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
+
+ { "toolbar-show-edit",
+ NULL,
+ N_("Edit _toolbar"),
+ NULL,
+ N_("Edit toolbar"),
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
+
+ { "pgp-encrypt",
+ NULL,
+ N_("PGP _Encrypt"),
+ NULL,
+ N_("Encrypt this message with PGP"),
+ NULL, NULL, "false", e_composer_set_action_state_with_changed_cb },
+
+ { "pgp-sign",
+ NULL,
+ N_("PGP _Sign"),
+ NULL,
+ N_("Sign this message with your PGP key"),
+ NULL, NULL, "false", e_composer_set_action_state_with_changed_cb },
+
+ { "picture-gallery",
+ "emblem-photos",
+ N_("_Picture Gallery"),
+ NULL,
+ N_("Show a collection of pictures that you can drag to your message"),
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
+
+ { "prioritize-message",
+ "mail-mark-important",
+ N_("_Prioritize Message"),
+ NULL,
+ N_("Set the message priority to high"),
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
+
+ { "request-read-receipt",
+ "preferences-system-notifications",
+ N_("Re_quest Read Receipt"),
+ NULL,
+ N_("Get delivery notification when your message is read"),
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
+
+ { "delivery-status-notification",
+ NULL,
+ N_("Request _Delivery Status Notification"),
+ NULL,
+ N_("Get delivery status notification for the message"),
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
+
+ { "smime-encrypt",
+ NULL,
+ N_("S/MIME En_crypt"),
+ NULL,
+ N_("Encrypt this message with S/MIME"),
+ NULL, NULL, "false", e_composer_set_action_state_with_changed_cb },
+
+ { "smime-sign",
+ NULL,
+ N_("S/MIME Sig_n"),
+ NULL,
+ N_("Sign this message with your S/MIME Signature Certificate"),
+ NULL, NULL, "false", e_composer_set_action_state_with_changed_cb },
+
+ { "toolbar-pgp-encrypt",
+ "gicon::EMsgComposer::pgp-encrypt",
+ N_("PGP _Encrypt"),
+ NULL,
+ N_("Encrypt this message with PGP"),
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
+
+ { "toolbar-pgp-sign",
+ "gicon::EMsgComposer::pgp-sign",
+ N_("PGP _Sign"),
+ NULL,
+ N_("Sign this message with your PGP key"),
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
+
+ { "toolbar-prioritize-message",
+ "emblem-important",
+ N_("_Prioritize Message"),
+ NULL,
+ N_("Set the message priority to high"),
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
+
+ { "toolbar-request-read-receipt",
+ "mail-forward",
+ N_("Re_quest Read Receipt"),
+ NULL,
+ N_("Get delivery notification when your message is read"),
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
+
+ { "toolbar-smime-encrypt",
+ "security-high",
+ N_("S/MIME En_crypt"),
+ NULL,
+ N_("Encrypt this message with S/MIME"),
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
+
+ { "toolbar-smime-sign",
+ "stock_signature",
+ N_("S/MIME Sig_n"),
+ NULL,
+ N_("Sign this message with your S/MIME Signature Certificate"),
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
+
+ { "view-bcc",
+ NULL,
+ N_("_Bcc Field"),
+ NULL,
+ N_("Toggles whether the BCC field is displayed"),
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
+
+ { "view-cc",
+ NULL,
+ N_("_Cc Field"),
+ NULL,
+ N_("Toggles whether the CC field is displayed"),
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
+
+ { "view-from-override",
+ NULL,
+ N_("_From Override Field"),
+ NULL,
+ N_("Toggles whether the From override field to change name or email address is displayed"),
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
+
+ { "view-mail-followup-to",
+ NULL,
+ N_("Mail-Follow_up-To Field"),
+ NULL,
+ N_("Toggles whether the Mail-Followup-To field is displayed"),
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
+
+ { "view-mail-reply-to",
+ NULL,
+ N_("Mail-R_eply-To Field"),
+ NULL,
+ N_("Toggles whether the Mail-Reply-To field is displayed"),
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
+
+ { "view-reply-to",
+ NULL,
+ N_("_Reply-To Field"),
+ NULL,
+ N_("Toggles whether the Reply-To field is displayed"),
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
+
+ { "visually-wrap-long-lines",
+ NULL,
+ N_("Visually _Wrap Long Lines"),
+ NULL,
+ N_("Whether to visually wrap long lines of text to avoid horizontal scrolling"),
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state }
+ };
+
GtkAccelGroup *accel_group;
- GtkUIManager *ui_manager;
+ EUIManager *ui_manager;
EHTMLEditor *editor;
EContentEditor *cnt_editor;
gboolean visible;
GSettings *settings;
- GtkAction *action;
- GIcon *gcr_gnupg_icon;
+ EUIAction *action;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
@@ -647,56 +653,27 @@ e_composer_actions_init (EMsgComposer *composer)
ui_manager = e_html_editor_get_ui_manager (editor);
/* Composer Actions */
- action_group = composer->priv->composer_actions;
- gtk_action_group_set_translation_domain (
- action_group, GETTEXT_PACKAGE);
- gtk_action_group_add_actions (
- action_group, entries,
- G_N_ELEMENTS (entries), composer);
- gtk_action_group_add_toggle_actions (
- action_group, toggle_entries,
- G_N_ELEMENTS (toggle_entries), composer);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+ e_ui_manager_add_actions (ui_manager, "composer", GETTEXT_PACKAGE,
+ entries, G_N_ELEMENTS (entries), composer);
+ e_ui_manager_add_actions (ui_manager, "composer", GETTEXT_PACKAGE,
+ toggle_entries, G_N_ELEMENTS (toggle_entries), composer);
/* Asynchronous Actions */
- action_group = composer->priv->async_actions;
- gtk_action_group_set_translation_domain (
- action_group, GETTEXT_PACKAGE);
- gtk_action_group_add_actions (
- action_group, async_entries,
- G_N_ELEMENTS (async_entries), composer);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+ e_ui_manager_add_actions (ui_manager, "async", GETTEXT_PACKAGE,
+ async_entries, G_N_ELEMENTS (async_entries), composer);
- /* Character Set Actions */
- action_group = composer->priv->charset_actions;
- gtk_action_group_set_translation_domain (
- action_group, GETTEXT_PACKAGE);
- e_charset_add_radio_actions (
- action_group, "charset-", composer->priv->charset,
- G_CALLBACK (action_charset_cb), composer);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+ action = e_ui_manager_get_action (ui_manager, "close");
+ e_ui_action_add_secondary_accel (action, "Escape");
- /* Fine Tuning */
-
- g_object_set (
- ACTION (ATTACH), "short-label", _("Attach"), NULL);
-
- g_object_set (
- ACTION (PICTURE_GALLERY), "is-important", TRUE, NULL);
-
- g_object_set (
- ACTION (SAVE_DRAFT), "short-label", _("Save Draft"), NULL);
+ action = e_ui_manager_get_action (ui_manager, "send");
+ e_ui_action_add_secondary_accel (action, "Send");
#define init_toolbar_option(x, always_visible) \
- gtk_action_set_visible (ACTION (TOOLBAR_ ## x), always_visible); \
+ e_ui_action_set_visible (ACTION (TOOLBAR_ ## x), always_visible); \
e_binding_bind_property ( \
ACTION (x), "active", \
ACTION (TOOLBAR_ ## x), "active", \
G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); \
- e_binding_bind_property ( \
- ACTION (x), "label", \
- ACTION (TOOLBAR_ ## x), "label", \
- G_BINDING_SYNC_CREATE); \
e_binding_bind_property ( \
ACTION (x), "tooltip", \
ACTION (TOOLBAR_ ## x), "tooltip", \
@@ -705,8 +682,8 @@ e_composer_actions_init (EMsgComposer *composer)
ACTION (x), "sensitive", \
ACTION (TOOLBAR_ ## x), "sensitive", \
G_BINDING_SYNC_CREATE); \
- g_signal_connect (ACTION (TOOLBAR_ ## x), "toggled", \
- G_CALLBACK (composer_actions_toolbar_option_toggled_cb), composer);
+ g_signal_connect (ACTION (TOOLBAR_ ## x), "notify::active", \
+ G_CALLBACK (composer_actions_toolbar_option_notify_active_cb), composer);
init_toolbar_option (PGP_SIGN, FALSE);
init_toolbar_option (PGP_ENCRYPT, FALSE);
@@ -733,35 +710,6 @@ e_composer_actions_init (EMsgComposer *composer)
g_object_unref (settings);
- /* Borrow a GnuPG icon from gcr to distinguish between GPG and S/MIME Sign/Encrypt actions */
- gcr_gnupg_icon = g_themed_icon_new ("gcr-gnupg");
- if (gcr_gnupg_icon) {
- GIcon *temp_icon;
- GIcon *action_icon;
- GEmblem *emblem;
-
- emblem = g_emblem_new (gcr_gnupg_icon);
-
- action = ACTION (TOOLBAR_PGP_SIGN);
- action_icon = g_themed_icon_new (gtk_action_get_icon_name (action));
- temp_icon = g_emblemed_icon_new (action_icon, emblem);
- g_object_unref (action_icon);
-
- gtk_action_set_gicon (action, temp_icon);
- g_object_unref (temp_icon);
-
- action = ACTION (TOOLBAR_PGP_ENCRYPT);
- action_icon = g_themed_icon_new (gtk_action_get_icon_name (action));
- temp_icon = g_emblemed_icon_new (action_icon, emblem);
- g_object_unref (action_icon);
-
- gtk_action_set_gicon (action, temp_icon);
- g_object_unref (temp_icon);
-
- g_object_unref (emblem);
- g_object_unref (gcr_gnupg_icon);
- }
-
e_binding_bind_property_full (
editor, "mode",
ACTION (PICTURE_GALLERY), "sensitive",
@@ -812,10 +760,10 @@ e_composer_actions_init (EMsgComposer *composer)
visible = FALSE;
#endif
- gtk_action_set_visible (ACTION (SMIME_ENCRYPT), visible);
- gtk_action_set_visible (ACTION (SMIME_SIGN), visible);
+ e_ui_action_set_visible (ACTION (SMIME_ENCRYPT), visible);
+ e_ui_action_set_visible (ACTION (SMIME_SIGN), visible);
- accel_group = gtk_ui_manager_get_accel_group (ui_manager);
+ accel_group = e_ui_manager_get_accel_group (ui_manager);
g_signal_connect (accel_group, "accel-activate",
G_CALLBACK (composer_actions_accel_activate_cb), composer);
}
diff --git a/src/composer/e-composer-private.c b/src/composer/e-composer-private.c
index 604345694a..37a637e38a 100644
--- a/src/composer/e-composer-private.c
+++ b/src/composer/e-composer-private.c
@@ -26,99 +26,19 @@
/* Initial height of the picture gallery. */
#define GALLERY_INITIAL_HEIGHT 150
-static void
-composer_setup_charset_menu (EMsgComposer *composer)
-{
- EHTMLEditor *editor;
- GtkUIManager *ui_manager;
- const gchar *path;
- GList *list;
- guint merge_id;
-
- editor = e_msg_composer_get_editor (composer);
- ui_manager = e_html_editor_get_ui_manager (editor);
- path = "/main-menu/options-menu/charset-menu";
- merge_id = gtk_ui_manager_new_merge_id (ui_manager);
-
- list = gtk_action_group_list_actions (composer->priv->charset_actions);
- list = g_list_sort (list, (GCompareFunc) e_action_compare_by_label);
-
- while (list != NULL) {
- GtkAction *action = list->data;
-
- gtk_ui_manager_add_ui (
- ui_manager, merge_id, path,
- gtk_action_get_name (action),
- gtk_action_get_name (action),
- GTK_UI_MANAGER_AUTO, FALSE);
-
- list = g_list_delete_link (list, list);
- }
-
- gtk_ui_manager_ensure_update (ui_manager);
-}
-
static void
composer_update_gallery_visibility (EMsgComposer *composer)
{
EHTMLEditor *editor;
- GtkToggleAction *toggle_action;
gboolean gallery_active;
gboolean is_html;
editor = e_msg_composer_get_editor (composer);
is_html = e_html_editor_get_mode (editor) == E_CONTENT_EDITOR_MODE_HTML;
+ gallery_active = e_ui_action_get_active (ACTION (PICTURE_GALLERY));
- toggle_action = GTK_TOGGLE_ACTION (ACTION (PICTURE_GALLERY));
- gallery_active = gtk_toggle_action_get_active (toggle_action);
-
- if (is_html && gallery_active) {
- gtk_widget_show (composer->priv->gallery_scrolled_window);
- gtk_widget_show (composer->priv->gallery_icon_view);
- } else {
- gtk_widget_hide (composer->priv->gallery_scrolled_window);
- gtk_widget_hide (composer->priv->gallery_icon_view);
- }
-}
-
-static GtkWidget *
-composer_construct_header_bar (EMsgComposer *composer,
- GtkWidget *menu_button)
-{
- GtkWidget *widget;
- GtkWidget *button;
- GtkHeaderBar *header_bar;
-
- widget = gtk_header_bar_new ();
- gtk_widget_show (widget);
- header_bar = GTK_HEADER_BAR (widget);
- gtk_header_bar_set_show_close_button (header_bar, TRUE);
-
- if (menu_button)
- gtk_header_bar_pack_end (header_bar, menu_button);
-
- button = e_header_bar_button_new (_("Send"), ACTION (SEND));
- e_header_bar_button_css_add_class (E_HEADER_BAR_BUTTON (button), "suggested-action");
- e_header_bar_button_set_show_icon_only (E_HEADER_BAR_BUTTON (button), FALSE);
- gtk_widget_show (button);
- gtk_header_bar_pack_start (header_bar, button);
-
- button = e_header_bar_button_new (NULL, ACTION (SAVE_DRAFT));
- e_header_bar_button_css_add_class (E_HEADER_BAR_BUTTON (button), "flat");
- gtk_widget_show (button);
- gtk_header_bar_pack_start (header_bar, button);
-
- button = e_header_bar_button_new (NULL, ACTION (PRIORITIZE_MESSAGE));
- e_header_bar_button_css_add_class (E_HEADER_BAR_BUTTON (button), "flat");
- gtk_widget_show (button);
- gtk_header_bar_pack_end (header_bar, button);
-
- button = e_header_bar_button_new (NULL, ACTION (REQUEST_READ_RECEIPT));
- e_header_bar_button_css_add_class (E_HEADER_BAR_BUTTON (button), "flat");
- gtk_widget_show (button);
- gtk_header_bar_pack_end (header_bar, button);
-
- return widget;
+ gtk_widget_set_visible (composer->priv->gallery_scrolled_window, is_html && gallery_active);
+ gtk_widget_set_visible (composer->priv->gallery_icon_view, is_html && gallery_active);
}
static gchar *
@@ -220,6 +140,124 @@ e_composer_from_changed_cb (EComposerFromHeader *header,
}
}
+static gchar *
+e_composer_find_data_file (const gchar *basename)
+{
+ gchar *filename;
+
+ g_return_val_if_fail (basename != NULL, NULL);
+
+ /* Support running directly from the source tree. */
+ filename = g_build_filename (".", basename, NULL);
+ if (g_file_test (filename, G_FILE_TEST_EXISTS))
+ return filename;
+ g_free (filename);
+
+ filename = g_build_filename (".", "data", "ui", basename, NULL);
+ if (g_file_test (filename, G_FILE_TEST_EXISTS))
+ return filename;
+ g_free (filename);
+
+ filename = g_build_filename ("..", "..", "..", "data", "ui", basename, NULL);
+ if (g_file_test (filename, G_FILE_TEST_EXISTS))
+ return filename;
+ g_free (filename);
+
+ filename = g_build_filename (EVOLUTION_UIDIR, basename, NULL);
+ if (g_file_test (filename, G_FILE_TEST_EXISTS))
+ return filename;
+ g_free (filename);
+
+ g_critical ("Could not locate '%s'", basename);
+
+ return NULL;
+}
+
+static gboolean
+e_composer_ui_manager_create_item_cb (EUIManager *ui_manager,
+ EUIElement *elem,
+ EUIAction *action,
+ EUIElementKind for_kind,
+ GObject **out_item,
+ gpointer user_data)
+{
+ EMsgComposer *self = user_data;
+ const gchar *name;
+
+ g_return_val_if_fail (E_IS_MSG_COMPOSER (self), FALSE);
+
+ name = g_action_get_name (G_ACTION (action));
+
+ if (!g_str_has_prefix (name, "EMsgComposer::"))
+ return FALSE;
+
+ #define is_action(_nm) (g_strcmp0 (name, (_nm)) == 0)
+
+ if (for_kind == E_UI_ELEMENT_KIND_MENU) {
+ if (is_action ("EMsgComposer::charset-menu"))
+ *out_item = G_OBJECT (g_menu_item_new_submenu (e_ui_action_get_label (action), G_MENU_MODEL (self->priv->charset_menu)));
+ else
+ g_warning ("%s: Unhandled menu action '%s'", G_STRFUNC, name);
+ } else if (for_kind == E_UI_ELEMENT_KIND_TOOLBAR) {
+ g_warning ("%s: Unhandled toolbar action '%s'", G_STRFUNC, name);
+ } else if (for_kind == E_UI_ELEMENT_KIND_HEADERBAR) {
+ if (is_action ("EMsgComposer::menu-button"))
+ *out_item = G_OBJECT (g_object_ref (self->priv->menu_button));
+ else
+ g_warning ("%s: Unhandled headerbar action '%s'", G_STRFUNC, name);
+ } else {
+ g_warning ("%s: Unhandled element kind '%d' for action '%s'", G_STRFUNC, (gint) for_kind, name);
+ }
+
+ #undef is_action
+
+ return TRUE;
+}
+
+static GIcon * /* (transfer full) */
+e_composer_mix_icons (const gchar *action_icon_name,
+ const gchar *emblem_icon_name)
+{
+ GIcon *action_icon;
+ GIcon *temp_icon;
+ GEmblem *emblem;
+
+ temp_icon = g_themed_icon_new (emblem_icon_name);
+ emblem = g_emblem_new (temp_icon);
+ g_object_unref (temp_icon);
+
+ action_icon = g_themed_icon_new (action_icon_name);
+ temp_icon = g_emblemed_icon_new (action_icon, emblem);
+ g_object_unref (action_icon);
+
+ g_object_unref (emblem);
+
+ return temp_icon;
+}
+
+static gboolean
+e_composer_ui_manager_create_gicon_cb (EUIManager *manager,
+ const gchar *name,
+ GIcon **out_gicon,
+ gpointer user_data)
+{
+ EMsgComposer *self = user_data;
+
+ g_return_val_if_fail (E_IS_MSG_COMPOSER (self), FALSE);
+
+ if (g_strcmp0 (name, "EMsgComposer::pgp-sign") == 0) {
+ /* Borrow a GnuPG icon from gcr to distinguish between GPG and S/MIME Sign/Encrypt actions */
+ *out_gicon = e_composer_mix_icons ("stock_signature", "gcr-gnupg");
+ return TRUE;
+ } else if (g_strcmp0 (name, "EMsgComposer::pgp-encrypt") == 0) {
+ /* Borrow a GnuPG icon from gcr to distinguish between GPG and S/MIME Sign/Encrypt actions */
+ *out_gicon = e_composer_mix_icons ("security-high", "gcr-gnupg");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
void
e_composer_private_constructed (EMsgComposer *composer)
{
@@ -230,21 +268,26 @@ e_composer_private_constructed (EMsgComposer *composer)
EClientCache *client_cache;
EHTMLEditor *editor;
EContentEditor *cnt_editor;
- GtkUIManager *ui_manager;
- GtkAction *action;
+ EUIManager *ui_manager;
+ EUIAction *action;
GtkWidget *container;
GtkWidget *widget;
- GtkWidget *menu_button = NULL;
GtkWindow *window;
GSettings *settings;
+ GObject *ui_item;
gchar *filename, *gallery_path;
guint ii;
- GError *error = NULL;
+ GError *local_error = NULL;
editor = e_msg_composer_get_editor (composer);
ui_manager = e_html_editor_get_ui_manager (editor);
cnt_editor = e_html_editor_get_content_editor (editor);
+ g_signal_connect_object (ui_manager, "create-item",
+ G_CALLBACK (e_composer_ui_manager_create_item_cb), composer, 0);
+ g_signal_connect_object (ui_manager, "create-gicon",
+ G_CALLBACK (e_composer_ui_manager_create_gicon_cb), composer, 0);
+
settings = e_util_ref_settings ("org.gnome.evolution.mail");
shell = e_msg_composer_get_shell (composer);
@@ -255,9 +298,11 @@ e_composer_private_constructed (EMsgComposer *composer)
priv->window_group = gtk_window_group_new ();
gtk_window_group_add_window (priv->window_group, window);
- priv->async_actions = gtk_action_group_new ("async");
- priv->charset_actions = gtk_action_group_new ("charset");
- priv->composer_actions = gtk_action_group_new ("composer");
+ priv->async_actions = e_ui_manager_get_action_group (ui_manager, "async");
+ priv->composer_actions = e_ui_manager_get_action_group (ui_manager, "composer");
+
+ priv->charset_menu = g_menu_new ();
+ e_charset_add_to_g_menu (priv->charset_menu, "composer.EMsgComposer::charset-menu");
priv->extra_hdr_names = g_ptr_array_new ();
priv->extra_hdr_values = g_ptr_array_new ();
@@ -277,17 +322,15 @@ e_composer_private_constructed (EMsgComposer *composer)
e_composer_actions_init (composer);
- filename = e_composer_find_data_file ("evolution-composer.ui");
- gtk_ui_manager_add_ui_from_file (ui_manager, filename, &error);
+ filename = e_composer_find_data_file ("evolution-composer.eui");
+ if (!e_ui_parser_merge_file (e_ui_manager_get_parser (ui_manager), filename, &local_error)) {
+ g_critical ("%s: Failed to merge .eui data: %s", G_STRFUNC, local_error ? local_error->message : "Unknown error");
+ }
+ g_clear_error (&local_error);
g_free (filename);
- composer_setup_charset_menu (composer);
-
- if (error != NULL) {
- /* Henceforth, bad things start happening. */
- g_critical ("%s", error->message);
- g_clear_error (&error);
- }
+ action = e_ui_manager_get_action (ui_manager, "EMsgComposer::charset-menu");
+ e_ui_action_set_state (action, g_variant_new_string (priv->charset));
/* Configure an EFocusTracker to manage selection actions. */
@@ -304,44 +347,21 @@ e_composer_private_constructed (EMsgComposer *composer)
container = widget;
/* Construct the main menu and headerbar. */
+ ui_item = e_html_editor_get_ui_object (editor, E_HTML_EDITOR_UI_OBJECT_MAIN_MENU);
+ widget = gtk_menu_bar_new_from_model (G_MENU_MODEL (ui_item));
- widget = e_html_editor_get_managed_widget (editor, "/main-menu");
- priv->menu_bar = e_menu_bar_new (GTK_MENU_BAR (widget), window, &menu_button);
+ priv->menu_bar = e_menu_bar_new (GTK_MENU_BAR (widget), window, &priv->menu_button);
gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
if (e_util_get_use_header_bar ()) {
- const gchar *items[] = {
- "/main-toolbar/pre-main-toolbar/send",
- "/main-toolbar/pre-main-toolbar/send-separator",
- "/main-toolbar/pre-main-toolbar/save-draft",
- "/main-toolbar/pre-main-toolbar/save-draft-separator",
- "/main-toolbar/prioritize-message-separator",
- "/main-toolbar/toolbar-prioritize-message",
- "/main-toolbar/toolbar-request-read-receipt"
- };
- widget = composer_construct_header_bar (composer, menu_button);
+ ui_item = e_ui_manager_create_item (ui_manager, "main-headerbar");
+ widget = GTK_WIDGET (ui_item);
gtk_window_set_titlebar (window, widget);
-
- /* Destroy items from the toolbar, which are in the header bar */
- for (ii = 0; ii < G_N_ELEMENTS (items); ii++) {
- widget = gtk_ui_manager_get_widget (ui_manager, items[ii]);
- if (widget)
- gtk_widget_destroy (widget);
- }
- } else {
- /* We set the send button as important to have a label */
- widget = gtk_ui_manager_get_widget (ui_manager, "/main-toolbar/pre-main-toolbar/send");
- gtk_tool_item_set_is_important (GTK_TOOL_ITEM (widget), TRUE);
-
- if (menu_button) {
- g_object_ref_sink (menu_button);
- gtk_widget_destroy (menu_button);
- }
}
- widget = e_html_editor_get_managed_widget (editor, "/main-toolbar");
+ ui_item = e_html_editor_get_ui_object (editor, E_HTML_EDITOR_UI_OBJECT_MAIN_TOOLBAR);
+ widget = GTK_WIDGET (ui_item);
gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
- gtk_widget_show (widget);
e_binding_bind_property (
ACTION (TOOLBAR_SHOW_MAIN), "active",
@@ -422,7 +442,7 @@ e_composer_private_constructed (EMsgComposer *composer)
G_CALLBACK (composer_update_gallery_visibility), composer);
g_signal_connect_swapped (
- ACTION (PICTURE_GALLERY), "toggled",
+ ACTION (PICTURE_GALLERY), "notify::active",
G_CALLBACK (composer_update_gallery_visibility), composer);
/* Initial sync */
@@ -553,12 +573,13 @@ e_composer_private_dispose (EMsgComposer *composer)
g_clear_object (&composer->priv->attachment_paned);
g_clear_object (&composer->priv->focus_tracker);
g_clear_object (&composer->priv->window_group);
- g_clear_object (&composer->priv->async_actions);
- g_clear_object (&composer->priv->charset_actions);
- g_clear_object (&composer->priv->composer_actions);
+ g_clear_object (&composer->priv->charset_menu);
g_clear_object (&composer->priv->gallery_scrolled_window);
g_clear_object (&composer->priv->redirect);
g_clear_object (&composer->priv->menu_bar);
+
+ composer->priv->async_actions = NULL;
+ composer->priv->composer_actions = NULL;
}
void
@@ -584,30 +605,6 @@ e_composer_private_finalize (EMsgComposer *composer)
g_clear_pointer (&composer->priv->content_hash, e_content_editor_util_free_content_hash);
}
-gchar *
-e_composer_find_data_file (const gchar *basename)
-{
- gchar *filename;
-
- g_return_val_if_fail (basename != NULL, NULL);
-
- /* Support running directly from the source tree. */
- filename = g_build_filename (".", basename, NULL);
- if (g_file_test (filename, G_FILE_TEST_EXISTS))
- return filename;
- g_free (filename);
-
- /* XXX This is kinda broken. */
- filename = g_build_filename (EVOLUTION_UIDIR, basename, NULL);
- if (g_file_test (filename, G_FILE_TEST_EXISTS))
- return filename;
- g_free (filename);
-
- g_critical ("Could not locate '%s'", basename);
-
- return NULL;
-}
-
gchar *
e_composer_get_default_charset (void)
{
diff --git a/src/composer/e-composer-private.h b/src/composer/e-composer-private.h
index c6a7cd23c5..0414a9fce0 100644
--- a/src/composer/e-composer-private.h
+++ b/src/composer/e-composer-private.h
@@ -36,10 +36,6 @@
#include "e-composer-actions.h"
#include "e-composer-header-table.h"
-#ifdef HAVE_XFREE
-#include
-#endif
-
/* Shorthand, requires a variable named "composer". */
#define ACTION(name) (E_COMPOSER_ACTION_##name (composer))
@@ -65,9 +61,9 @@ struct _EMsgComposerPrivate {
EFocusTracker *focus_tracker;
GtkWindowGroup *window_group;
- GtkActionGroup *async_actions;
- GtkActionGroup *charset_actions;
- GtkActionGroup *composer_actions;
+ GMenu *charset_menu;
+ EUIActionGroup *async_actions; /* owned by EHTMLEditor::ui-manager */
+ EUIActionGroup *composer_actions; /* owned by EHTMLEditor::ui-manager */
GPtrArray *extra_hdr_names;
GPtrArray *extra_hdr_values;
@@ -80,6 +76,7 @@ struct _EMsgComposerPrivate {
GtkWidget *address_dialog;
EMenuBar *menu_bar;
+ GtkWidget *menu_button; /* owned by menu_bar */
gchar *mime_type;
gchar *mime_body;
@@ -138,7 +135,6 @@ void e_composer_private_finalize (EMsgComposer *composer);
/* Private Utilities */
void e_composer_actions_init (EMsgComposer *composer);
-gchar * e_composer_find_data_file (const gchar *basename);
gchar * e_composer_get_default_charset (void);
gchar * e_composer_decode_clue_value (const gchar *encoded_value);
gchar * e_composer_encode_clue_value (const gchar *decoded_value);
diff --git a/src/composer/e-msg-composer.c b/src/composer/e-msg-composer.c
index 403eaaf255..6399861f0e 100644
--- a/src/composer/e-msg-composer.c
+++ b/src/composer/e-msg-composer.c
@@ -2136,7 +2136,7 @@ msg_composer_mail_identity_changed_cb (EMsgComposer *composer)
ESourceSMIME *smime;
EComposerHeaderTable *table;
EContentEditor *cnt_editor;
- GtkToggleAction *action;
+ EUIAction *action;
ESource *source;
gboolean active;
gboolean can_sign;
@@ -2200,25 +2200,25 @@ msg_composer_mail_identity_changed_cb (EMsgComposer *composer)
to be set, when the default account has it set, but the other not. */
composer_realized = gtk_widget_get_realized (GTK_WIDGET (composer));
- action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN));
- active = composer_realized && gtk_toggle_action_get_active (action);
+ action = ACTION (PGP_SIGN);
+ active = composer_realized && e_ui_action_get_active (action);
active |= (can_sign && pgp_sign);
- gtk_toggle_action_set_active (action, active);
+ e_ui_action_set_active (action, active);
- action = GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT));
- active = composer_realized && gtk_toggle_action_get_active (action);
+ action = ACTION (PGP_ENCRYPT);
+ active = composer_realized && e_ui_action_get_active (action);
active |= pgp_encrypt;
- gtk_toggle_action_set_active (action, active);
+ e_ui_action_set_active (action, active);
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN));
- active = composer_realized && gtk_toggle_action_get_active (action);
+ action = ACTION (SMIME_SIGN);
+ active = composer_realized && e_ui_action_get_active (action);
active |= (can_sign && smime_sign);
- gtk_toggle_action_set_active (action, active);
+ e_ui_action_set_active (action, active);
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT));
- active = composer_realized && gtk_toggle_action_get_active (action);
+ action = ACTION (SMIME_ENCRYPT);
+ active = composer_realized && e_ui_action_get_active (action);
active |= smime_encrypt;
- gtk_toggle_action_set_active (action, active);
+ e_ui_action_set_active (action, active);
was_disable_signature = composer->priv->disable_signature;
@@ -2451,10 +2451,10 @@ msg_composer_delete_event_cb (EMsgComposer *composer)
{
/* If the "async" action group is insensitive, it means an
* asynchronous operation is in progress. Block the event. */
- if (!gtk_action_group_get_sensitive (composer->priv->async_actions))
+ if (!e_ui_action_group_get_sensitive (composer->priv->async_actions))
return TRUE;
- gtk_action_activate (ACTION (CLOSE));
+ g_action_activate (G_ACTION (ACTION (CLOSE)), NULL);
return TRUE;
}
@@ -2463,25 +2463,25 @@ static void
msg_composer_realize_cb (EMsgComposer *composer)
{
GSettings *settings;
- GtkAction *action;
+ EUIAction *action;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
action = ACTION (TOOLBAR_PGP_SIGN);
- if (gtk_action_get_visible (action) && !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
- gtk_action_set_visible (action, FALSE);
+ if (e_ui_action_get_visible (action) && !e_ui_action_get_active (action))
+ e_ui_action_set_visible (action, FALSE);
action = ACTION (TOOLBAR_PGP_ENCRYPT);
- if (gtk_action_get_visible (action) && !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
- gtk_action_set_visible (action, FALSE);
+ if (e_ui_action_get_visible (action) && !e_ui_action_get_active (action))
+ e_ui_action_set_visible (action, FALSE);
action = ACTION (TOOLBAR_SMIME_SIGN);
- if (gtk_action_get_visible (action) && !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
- gtk_action_set_visible (action, FALSE);
+ if (e_ui_action_get_visible (action) && !e_ui_action_get_active (action))
+ e_ui_action_set_visible (action, FALSE);
action = ACTION (TOOLBAR_SMIME_ENCRYPT);
- if (gtk_action_get_visible (action) && !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
- gtk_action_set_visible (action, FALSE);
+ if (e_ui_action_get_visible (action) && !e_ui_action_get_active (action))
+ e_ui_action_set_visible (action, FALSE);
settings = e_util_ref_settings ("org.gnome.evolution.mail");
@@ -2501,11 +2501,8 @@ msg_composer_realize_cb (EMsgComposer *composer)
key_id = e_source_openpgp_dup_key_id (e_source_get_extension (source, E_SOURCE_EXTENSION_OPENPGP));
if (key_id && *key_id) {
- action = ACTION (TOOLBAR_PGP_SIGN);
- gtk_action_set_visible (action, TRUE);
-
- action = ACTION (TOOLBAR_PGP_ENCRYPT);
- gtk_action_set_visible (action, TRUE);
+ e_ui_action_set_visible (ACTION (TOOLBAR_PGP_SIGN), TRUE);
+ e_ui_action_set_visible (ACTION (TOOLBAR_PGP_ENCRYPT), TRUE);
}
g_free (key_id);
@@ -2519,12 +2516,12 @@ msg_composer_realize_cb (EMsgComposer *composer)
certificate = e_source_smime_dup_signing_certificate (smime_extension);
if (certificate && *certificate)
- gtk_action_set_visible (ACTION (TOOLBAR_SMIME_SIGN), TRUE);
+ e_ui_action_set_visible (ACTION (TOOLBAR_SMIME_SIGN), TRUE);
g_free (certificate);
certificate = e_source_smime_dup_encryption_certificate (smime_extension);
if (certificate && *certificate)
- gtk_action_set_visible (ACTION (TOOLBAR_SMIME_ENCRYPT), TRUE);
+ e_ui_action_set_visible (ACTION (TOOLBAR_SMIME_ENCRYPT), TRUE);
g_free (certificate);
}
@@ -2548,7 +2545,7 @@ msg_composer_prepare_for_quit_cb (EShell *shell,
g_object_weak_ref (
G_OBJECT (composer), (GWeakNotify)
g_object_unref, activity);
- gtk_action_activate (ACTION (SAVE_DRAFT));
+ g_action_activate (G_ACTION (ACTION (SAVE_DRAFT)), NULL);
}
}
@@ -2780,13 +2777,12 @@ msg_composer_constructed (GObject *object)
EComposerHeaderTable *table;
EHTMLEditor *editor;
EContentEditor *cnt_editor;
- GtkUIManager *ui_manager;
- GtkToggleAction *action;
+ EUIManager *ui_manager;
+ EUIAction *action;
GtkTargetList *target_list;
GtkTargetEntry *targets;
gint n_targets;
GSettings *settings;
- const gchar *id;
gboolean active;
/* Chain up to parent's constructed() method. */
@@ -2797,12 +2793,14 @@ msg_composer_constructed (GObject *object)
g_return_if_fail (E_IS_HTML_EDITOR (composer->priv->editor));
shell = e_msg_composer_get_shell (composer);
-
- e_composer_private_constructed (composer);
-
editor = e_msg_composer_get_editor (composer);
cnt_editor = e_html_editor_get_content_editor (editor);
ui_manager = e_html_editor_get_ui_manager (editor);
+
+ e_ui_manager_freeze (ui_manager);
+
+ e_composer_private_constructed (composer);
+
attachment_view = e_msg_composer_get_attachment_view (composer);
table = E_COMPOSER_HEADER_TABLE (composer->priv->header_table);
@@ -2848,13 +2846,13 @@ msg_composer_constructed (GObject *object)
/* FIXME This should be an EMsgComposer property. */
settings = e_util_ref_settings ("org.gnome.evolution.mail");
- action = GTK_TOGGLE_ACTION (ACTION (REQUEST_READ_RECEIPT));
+ action = ACTION (REQUEST_READ_RECEIPT);
active = g_settings_get_boolean (settings, "composer-request-receipt");
- gtk_toggle_action_set_active (action, active);
+ e_ui_action_set_active (action, active);
- action = GTK_TOGGLE_ACTION (ACTION (DELIVERY_STATUS_NOTIFICATION));
+ action = ACTION (DELIVERY_STATUS_NOTIFICATION);
active = g_settings_get_boolean (settings, "composer-request-dsn");
- gtk_toggle_action_set_active (action, active);
+ e_ui_action_set_active (action, active);
g_object_unref (settings);
@@ -2951,12 +2949,12 @@ msg_composer_constructed (GObject *object)
gtk_target_table_free (targets, n_targets);
- id = "org.gnome.evolution.composer";
- e_plugin_ui_register_manager (ui_manager, id, composer);
- e_plugin_ui_enable_manager (ui_manager, id);
+ e_plugin_ui_register_manager (ui_manager, "org.gnome.evolution.composer", composer);
e_extensible_load_extensions (E_EXTENSIBLE (composer));
+ e_ui_manager_thaw (ui_manager);
+
e_msg_composer_set_body_text (composer, "", TRUE);
}
@@ -3058,18 +3056,6 @@ msg_composer_key_press_event (GtkWidget *widget,
e_msg_composer_get_header_table (composer),
E_COMPOSER_HEADER_SUBJECT)->input_widget;
-#ifdef HAVE_XFREE
- if (event->keyval == XF86XK_Send) {
- e_msg_composer_send (composer);
- return TRUE;
- }
-#endif /* HAVE_XFREE */
-
- if (event->keyval == GDK_KEY_Escape) {
- gtk_action_activate (ACTION (CLOSE));
- return TRUE;
- }
-
if (event->keyval == GDK_KEY_Tab && gtk_widget_is_focus (input_widget)) {
e_content_editor_grab_focus (cnt_editor);
return TRUE;
@@ -3781,7 +3767,7 @@ handle_multipart_signed (EMsgComposer *composer,
CamelContentType *content_type;
CamelDataWrapper *content;
CamelMimePart *mime_part;
- GtkToggleAction *action = NULL;
+ EUIAction *action = NULL;
const gchar *protocol;
content = CAMEL_DATA_WRAPPER (multipart);
@@ -3791,20 +3777,20 @@ handle_multipart_signed (EMsgComposer *composer,
if (protocol == NULL) {
action = NULL;
} else if (g_ascii_strcasecmp (protocol, "application/pgp-signature") == 0) {
- if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN))) &&
- !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT))))
- action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN));
+ if (!e_ui_action_get_active (ACTION (SMIME_SIGN)) &&
+ !e_ui_action_get_active (ACTION (SMIME_ENCRYPT)))
+ action = ACTION (PGP_SIGN);
} else if (g_ascii_strcasecmp (protocol, "application/pkcs7-signature") == 0 ||
g_ascii_strcasecmp (protocol, "application/xpkcs7signature") == 0 ||
g_ascii_strcasecmp (protocol, "application/xpkcs7-signature") == 0 ||
g_ascii_strcasecmp (protocol, "application/x-pkcs7-signature") == 0) {
- if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (PGP_SIGN))) &&
- !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT))))
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN));
+ if (!e_ui_action_get_active (ACTION (PGP_SIGN)) &&
+ !e_ui_action_get_active (ACTION (PGP_ENCRYPT)))
+ action = ACTION (SMIME_SIGN);
}
if (action)
- gtk_toggle_action_set_active (action, TRUE);
+ e_ui_action_set_active (action, TRUE);
mime_part = camel_multipart_get_part (
multipart, CAMEL_MULTIPART_SIGNED_CONTENT);
@@ -3881,28 +3867,28 @@ handle_multipart_encrypted (EMsgComposer *composer,
CamelMimePart *mime_part;
CamelSession *session;
CamelCipherValidity *valid;
- GtkToggleAction *action = NULL;
+ EUIAction *action = NULL;
const gchar *protocol;
content_type = camel_mime_part_get_content_type (multipart);
protocol = camel_content_type_param (content_type, "protocol");
if (protocol && g_ascii_strcasecmp (protocol, "application/pgp-encrypted") == 0) {
- if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN))) &&
- !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT))))
- action = GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT));
+ if (!e_ui_action_get_active (ACTION (SMIME_SIGN)) &&
+ !e_ui_action_get_active (ACTION (SMIME_ENCRYPT)))
+ action = ACTION (PGP_ENCRYPT);
} else if (content_type && (
camel_content_type_is (content_type, "application", "pkcs7-mime") ||
camel_content_type_is (content_type, "application", "xpkcs7mime") ||
camel_content_type_is (content_type, "application", "xpkcs7-mime") ||
camel_content_type_is (content_type, "application", "x-pkcs7-mime"))) {
- if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (PGP_SIGN))) &&
- !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT))))
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT));
+ if (!e_ui_action_get_active (ACTION (PGP_SIGN)) &&
+ !e_ui_action_get_active (ACTION (PGP_ENCRYPT)))
+ action = ACTION (SMIME_ENCRYPT);
}
if (action)
- gtk_toggle_action_set_active (action, TRUE);
+ e_ui_action_set_active (action, TRUE);
session = e_msg_composer_ref_session (composer);
cipher = camel_gpg_context_new (session);
@@ -4372,7 +4358,6 @@ e_msg_composer_setup_with_message (EMsgComposer *composer,
ESource *source = NULL;
EHTMLEditor *editor;
EContentEditor *cnt_editor;
- GtkToggleAction *action;
gchar *identity_uid;
gint len, i;
guint jj, jjlen;
@@ -4613,17 +4598,13 @@ e_msg_composer_setup_with_message (EMsgComposer *composer,
e_html_editor_set_mode (editor, mode);
} else if (g_ascii_strcasecmp (flags[i], "pgp-sign") == 0) {
- action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN));
- gtk_toggle_action_set_active (action, TRUE);
+ e_ui_action_set_active (ACTION (PGP_SIGN), TRUE);
} else if (g_ascii_strcasecmp (flags[i], "pgp-encrypt") == 0) {
- action = GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT));
- gtk_toggle_action_set_active (action, TRUE);
+ e_ui_action_set_active (ACTION (PGP_ENCRYPT), TRUE);
} else if (g_ascii_strcasecmp (flags[i], "smime-sign") == 0) {
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN));
- gtk_toggle_action_set_active (action, TRUE);
+ e_ui_action_set_active (ACTION (SMIME_SIGN), TRUE);
} else if (g_ascii_strcasecmp (flags[i], "smime-encrypt") == 0) {
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT));
- gtk_toggle_action_set_active (action, TRUE);
+ e_ui_action_set_active (ACTION (SMIME_ENCRYPT), TRUE);
}
}
g_strfreev (flags);
@@ -4666,24 +4647,20 @@ e_msg_composer_setup_with_message (EMsgComposer *composer,
}
if (g_strcmp0 (camel_medium_get_header (CAMEL_MEDIUM (message), "X-Evolution-Request-DSN"), "1") == 0) {
- action = GTK_TOGGLE_ACTION (ACTION (DELIVERY_STATUS_NOTIFICATION));
- gtk_toggle_action_set_active (action, TRUE);
+ e_ui_action_set_active (ACTION (DELIVERY_STATUS_NOTIFICATION), TRUE);
}
/* Remove any other X-Evolution-* headers that may have been set */
camel_name_value_array_free (mail_tool_remove_xevolution_headers (message));
/* Check for receipt request */
- if (camel_medium_get_header (
- CAMEL_MEDIUM (message), "Disposition-Notification-To")) {
- action = GTK_TOGGLE_ACTION (ACTION (REQUEST_READ_RECEIPT));
- gtk_toggle_action_set_active (action, TRUE);
+ if (camel_medium_get_header (CAMEL_MEDIUM (message), "Disposition-Notification-To")) {
+ e_ui_action_set_active (ACTION (REQUEST_READ_RECEIPT), TRUE);
}
/* Check for mail priority */
if (camel_medium_get_header (CAMEL_MEDIUM (message), "X-Priority")) {
- action = GTK_TOGGLE_ACTION (ACTION (PRIORITIZE_MESSAGE));
- gtk_toggle_action_set_active (action, TRUE);
+ e_ui_action_set_active (ACTION (PRIORITIZE_MESSAGE), TRUE);
}
/* set extra headers */
@@ -4756,9 +4733,7 @@ e_msg_composer_setup_with_message (EMsgComposer *composer,
camel_content_type_is (content_type, "application", "xpkcs7-mime") ||
camel_content_type_is (content_type, "application", "x-pkcs7-mime"))) {
#ifdef ENABLE_SMIME
- gtk_toggle_action_set_active (
- GTK_TOGGLE_ACTION (
- ACTION (SMIME_ENCRYPT)), TRUE);
+ e_ui_action_set_active (ACTION (SMIME_ENCRYPT), TRUE);
is_smime_encrypted = TRUE;
#endif
}
@@ -5076,16 +5051,13 @@ msg_composer_alert_response_cb (EAlert *alert,
{
if (response_id == GTK_RESPONSE_ACCEPT) {
EMsgComposer *composer = user_data;
- GtkAction *action;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
- action = ACTION (PGP_ENCRYPT);
- gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), FALSE);
+ e_ui_action_set_active (ACTION (PGP_ENCRYPT), FALSE);
#ifdef ENABLE_SMIME
- action = ACTION (SMIME_ENCRYPT);
- gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), FALSE);
+ e_ui_action_set_active (ACTION (SMIME_ENCRYPT), FALSE);
#endif
e_msg_composer_send (composer);
@@ -5110,9 +5082,10 @@ e_msg_composer_claim_no_build_message_error (EMsgComposer *composer,
alert = e_alert_new ("mail-composer:no-build-message", error->message, NULL);
if (is_send_op && g_error_matches (error, CAMEL_CIPHER_CONTEXT_ERROR, CAMEL_CIPHER_CONTEXT_ERROR_KEY_NOT_FOUND)) {
- GtkAction *action;
+ EUIAction *action;
- action = gtk_action_new ("msg-composer-alert-action-0", _("Send _without encryption"), NULL, NULL);
+ action = e_ui_action_new ("msg-composer-map", "msg-composer-alert-action-0", NULL);
+ e_ui_action_set_label (action, _("Send _without encryption"));
e_alert_add_action (alert, action, GTK_RESPONSE_ACCEPT, FALSE);
g_object_unref (action);
@@ -6043,13 +6016,8 @@ e_msg_composer_set_body (EMsgComposer *composer,
priv->mime_type = g_strdup (mime_type);
if (!msg_composer_get_can_sign (composer)) {
- GtkToggleAction *action;
-
- action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN));
- gtk_toggle_action_set_active (action, FALSE);
-
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN));
- gtk_toggle_action_set_active (action, FALSE);
+ e_ui_action_set_active (ACTION (PGP_SIGN), FALSE);
+ e_ui_action_set_active (ACTION (SMIME_SIGN), FALSE);
}
}
@@ -6386,7 +6354,6 @@ e_msg_composer_get_message (EMsgComposer *composer,
gpointer user_data)
{
GTask *task;
- GtkAction *action;
ComposerFlags flags = 0;
EHTMLEditor *editor;
@@ -6398,33 +6365,26 @@ e_msg_composer_get_message (EMsgComposer *composer,
e_html_editor_get_mode (editor) == E_CONTENT_EDITOR_MODE_MARKDOWN_HTML)
flags |= COMPOSER_FLAG_HTML_CONTENT;
- action = ACTION (PRIORITIZE_MESSAGE);
- if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ if (e_ui_action_get_active (ACTION (PRIORITIZE_MESSAGE)))
flags |= COMPOSER_FLAG_PRIORITIZE_MESSAGE;
- action = ACTION (REQUEST_READ_RECEIPT);
- if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ if (e_ui_action_get_active (ACTION (REQUEST_READ_RECEIPT)))
flags |= COMPOSER_FLAG_REQUEST_READ_RECEIPT;
- action = ACTION (DELIVERY_STATUS_NOTIFICATION);
- if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ if (e_ui_action_get_active (ACTION (DELIVERY_STATUS_NOTIFICATION)))
flags |= COMPOSER_FLAG_DELIVERY_STATUS_NOTIFICATION;
- action = ACTION (PGP_SIGN);
- if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ if (e_ui_action_get_active (ACTION (PGP_SIGN)))
flags |= COMPOSER_FLAG_PGP_SIGN;
- action = ACTION (PGP_ENCRYPT);
- if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ if (e_ui_action_get_active (ACTION (PGP_ENCRYPT)))
flags |= COMPOSER_FLAG_PGP_ENCRYPT;
#ifdef ENABLE_SMIME
- action = ACTION (SMIME_SIGN);
- if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ if (e_ui_action_get_active (ACTION (SMIME_SIGN)))
flags |= COMPOSER_FLAG_SMIME_SIGN;
- action = ACTION (SMIME_ENCRYPT);
- if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ if (e_ui_action_get_active (ACTION (SMIME_ENCRYPT)))
flags |= COMPOSER_FLAG_SMIME_ENCRYPT;
#endif
@@ -6512,40 +6472,32 @@ e_msg_composer_get_message_draft (EMsgComposer *composer,
{
GTask *task;
ComposerFlags flags = COMPOSER_FLAG_SAVE_DRAFT;
- GtkAction *action;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
/* We want to save HTML content everytime when we save as draft */
flags |= COMPOSER_FLAG_SAVE_DRAFT;
- action = ACTION (PRIORITIZE_MESSAGE);
- if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ if (e_ui_action_get_active (ACTION (PRIORITIZE_MESSAGE)))
flags |= COMPOSER_FLAG_PRIORITIZE_MESSAGE;
- action = ACTION (REQUEST_READ_RECEIPT);
- if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ if (e_ui_action_get_active (ACTION (REQUEST_READ_RECEIPT)))
flags |= COMPOSER_FLAG_REQUEST_READ_RECEIPT;
- action = ACTION (DELIVERY_STATUS_NOTIFICATION);
- if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ if (e_ui_action_get_active (ACTION (DELIVERY_STATUS_NOTIFICATION)))
flags |= COMPOSER_FLAG_DELIVERY_STATUS_NOTIFICATION;
- action = ACTION (PGP_SIGN);
- if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ if (e_ui_action_get_active (ACTION (PGP_SIGN)))
flags |= COMPOSER_FLAG_PGP_SIGN;
- action = ACTION (PGP_ENCRYPT);
- if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ if (e_ui_action_get_active (ACTION (PGP_ENCRYPT)))
flags |= COMPOSER_FLAG_PGP_ENCRYPT;
#ifdef ENABLE_SMIME
- action = ACTION (SMIME_SIGN);
- if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ if (e_ui_action_get_active (ACTION (SMIME_SIGN)))
flags |= COMPOSER_FLAG_SMIME_SIGN;
- action = ACTION (SMIME_ENCRYPT);
- if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ if (e_ui_action_get_active (ACTION (SMIME_ENCRYPT)))
flags |= COMPOSER_FLAG_SMIME_ENCRYPT;
#endif
@@ -6785,7 +6737,7 @@ e_msg_composer_can_close (EMsgComposer *composer,
/* this means that there is an async operation running,
* in which case the composer cannot be closed */
- if (!gtk_action_group_get_sensitive (composer->priv->async_actions))
+ if (!e_ui_action_group_get_sensitive (composer->priv->async_actions))
return FALSE;
if (!e_content_editor_get_changed (cnt_editor) ||
@@ -6812,7 +6764,7 @@ e_msg_composer_can_close (EMsgComposer *composer,
case GTK_RESPONSE_YES:
e_msg_composer_request_close (composer);
if (can_save_draft)
- gtk_action_activate (ACTION (SAVE_DRAFT));
+ g_action_activate (G_ACTION (ACTION (SAVE_DRAFT)), NULL);
break;
case GTK_RESPONSE_NO:
@@ -7113,8 +7065,8 @@ e_msg_composer_check_autocrypt (EMsgComposer *composer,
if (alert_bar)
e_alert_bar_remove_alert_by_tag (alert_bar, "mail-composer:info-autocrypt-header-too-large");
- if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN))) ||
- gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT)))) {
+ if (e_ui_action_get_active (ACTION (SMIME_SIGN)) ||
+ e_ui_action_get_active (ACTION (SMIME_ENCRYPT))) {
/* Autocrypt is about GPG, thus ignore it when the user uses S/MIME */
return;
}
@@ -7206,8 +7158,8 @@ e_msg_composer_check_autocrypt (EMsgComposer *composer,
if (send_prefer_encrypt && sender_prefer_encrypt && msg_composer_get_can_sign (composer)) {
/* Set both sign & encrypt, not only encrypt */
- gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (ACTION (PGP_SIGN)), TRUE);
- gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT)), TRUE);
+ e_ui_action_set_active (ACTION (PGP_SIGN), TRUE);
+ e_ui_action_set_active (ACTION (PGP_ENCRYPT), TRUE);
}
g_free (from_email);
@@ -7223,19 +7175,10 @@ e_msg_composer_set_is_imip (EMsgComposer *composer,
composer->priv->is_imip = is_imip;
if (!msg_composer_get_can_sign (composer)) {
- GtkToggleAction *action;
-
- action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN));
- gtk_toggle_action_set_active (action, FALSE);
-
- action = GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT));
- gtk_toggle_action_set_active (action, FALSE);
-
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN));
- gtk_toggle_action_set_active (action, FALSE);
-
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT));
- gtk_toggle_action_set_active (action, FALSE);
+ e_ui_action_set_active (ACTION (PGP_SIGN), FALSE);
+ e_ui_action_set_active (ACTION (PGP_ENCRYPT), FALSE);
+ e_ui_action_set_active (ACTION (SMIME_SIGN), FALSE);
+ e_ui_action_set_active (ACTION (SMIME_ENCRYPT), FALSE);
}
}
diff --git a/src/composer/evolution-composer.ui b/src/composer/evolution-composer.ui
deleted file mode 100644
index f46dd11269..0000000000
--- a/src/composer/evolution-composer.ui
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/e-util/CMakeLists.txt b/src/e-util/CMakeLists.txt
index 326f2acae7..27d8ffcb6f 100644
--- a/src/e-util/CMakeLists.txt
+++ b/src/e-util/CMakeLists.txt
@@ -8,7 +8,6 @@ add_error_files(e-util
)
set(uifiles
- e-html-editor-manager.ui
e-send-options.ui
e-table-config.ui
e-timezone-dialog.ui
@@ -117,10 +116,8 @@ set(SOURCES
e-dialog-utils.c
e-dialog-widgets.c
e-ellipsized-combo-box-text.c
- e-emoticon-action.c
e-emoticon-chooser-menu.c
e-emoticon-chooser.c
- e-emoticon-tool-button.c
e-emoticon.c
e-event.c
e-file-request.c
@@ -175,7 +172,6 @@ set(SOURCES
e-markdown-utils.c
e-marshal.c
e-menu-bar.c
- e-menu-tool-action.c
e-menu-tool-button.c
e-misc-utils.c
e-mktemp.c
@@ -194,7 +190,6 @@ set(SOURCES
e-plugin-ui.c
e-plugin.c
e-poolv.c
- e-popup-action.c
e-popup-menu.c
e-port-entry.c
e-preferences-window.c
@@ -278,6 +273,11 @@ set(SOURCES
e-tree-table-adapter.c
e-tree-view-frame.c
e-tree.c
+ e-ui-action.c
+ e-ui-action-group.c
+ e-ui-manager.c
+ e-ui-menu.c
+ e-ui-parser.c
e-unicode.c
e-url-entry.c
e-util-private.h
@@ -402,10 +402,8 @@ set(HEADERS
e-dialog-utils.h
e-dialog-widgets.h
e-ellipsized-combo-box-text.h
- e-emoticon-action.h
e-emoticon-chooser-menu.h
e-emoticon-chooser.h
- e-emoticon-tool-button.h
e-emoticon.h
e-event.h
e-file-request.h
@@ -456,7 +454,6 @@ set(HEADERS
e-markdown-editor.h
e-markdown-utils.h
e-menu-bar.h
- e-menu-tool-action.h
e-menu-tool-button.h
e-misc-utils.h
e-mktemp.h
@@ -475,7 +472,6 @@ set(HEADERS
e-plugin-ui.h
e-plugin.h
e-poolv.h
- e-popup-action.h
e-popup-menu.h
e-port-entry.h
e-preferences-window.h
@@ -561,6 +557,11 @@ set(HEADERS
e-tree-table-adapter.h
e-tree-view-frame.h
e-tree.h
+ e-ui-action.h
+ e-ui-action-group.h
+ e-ui-manager.h
+ e-ui-menu.h
+ e-ui-parser.h
e-unicode.h
e-url-entry.h
e-util-enums.h
diff --git a/src/e-util/e-action-combo-box.c b/src/e-util/e-action-combo-box.c
index cf05e1234e..135463a6c8 100644
--- a/src/e-util/e-action-combo-box.c
+++ b/src/e-util/e-action-combo-box.c
@@ -18,10 +18,13 @@
#include "evolution-config.h"
-#include "e-action-combo-box.h"
+#include
+
+#include "e-ui-action.h"
+#include "e-ui-action-group.h"
#include "e-misc-utils.h"
-#include
+#include "e-action-combo-box.h"
enum {
COLUMN_ACTION,
@@ -30,14 +33,15 @@ enum {
enum {
PROP_0,
- PROP_ACTION
+ PROP_ACTION,
+ PROP_CURRENT_VALUE
};
struct _EActionComboBoxPrivate {
- GtkRadioAction *action;
- GtkActionGroup *action_group;
+ EUIAction *action;
+ EUIActionGroup *action_group;
GHashTable *index;
- guint changed_handler_id; /* action::changed */
+ guint notify_state_handler_id; /* action::notify::state */
guint group_sensitive_handler_id; /* action-group::sensitive */
guint group_visible_handler_id; /* action-group::visible */
gboolean group_has_icons;
@@ -47,20 +51,29 @@ struct _EActionComboBoxPrivate {
G_DEFINE_TYPE_WITH_PRIVATE (EActionComboBox, e_action_combo_box, GTK_TYPE_COMBO_BOX)
static void
-action_combo_box_action_changed_cb (GtkRadioAction *action,
- GtkRadioAction *current,
- EActionComboBox *combo_box)
+action_combo_box_action_notify_state_cb (EUIAction *action,
+ GParamSpec *param,
+ EActionComboBox *combo_box)
{
- GtkTreeRowReference *reference;
+ GtkTreeRowReference *reference = NULL;
GtkTreeModel *model;
GtkTreePath *path;
+ GVariant *state;
GtkTreeIter iter;
gboolean valid;
- reference = g_hash_table_lookup (
- combo_box->priv->index, GINT_TO_POINTER (
- gtk_radio_action_get_current_value (current)));
- g_return_if_fail (reference != NULL);
+ state = g_action_get_state (G_ACTION (action));
+ if (state && g_variant_is_of_type (state, G_VARIANT_TYPE_INT32)) {
+ reference = g_hash_table_lookup (combo_box->priv->index, GINT_TO_POINTER (g_variant_get_int32 (state)));
+ g_return_if_fail (reference != NULL);
+ } else {
+ g_warn_if_reached ();
+ }
+
+ g_clear_pointer (&state, g_variant_unref);
+
+ if (!reference)
+ return;
model = gtk_tree_row_reference_get_model (reference);
path = gtk_tree_row_reference_get_path (reference);
@@ -72,14 +85,14 @@ action_combo_box_action_changed_cb (GtkRadioAction *action,
}
static void
-action_combo_box_action_group_notify_cb (GtkActionGroup *action_group,
+action_combo_box_action_group_notify_cb (EUIActionGroup *action_group,
GParamSpec *pspec,
EActionComboBox *combo_box)
{
- g_object_set (
- combo_box, "sensitive",
- gtk_action_group_get_sensitive (action_group), "visible",
- gtk_action_group_get_visible (action_group), NULL);
+ g_object_set (combo_box,
+ "sensitive", e_ui_action_group_get_sensitive (action_group),
+ "visible", e_ui_action_group_get_visible (action_group),
+ NULL);
}
static void
@@ -89,11 +102,7 @@ action_combo_box_render_pixbuf (GtkCellLayout *layout,
GtkTreeIter *iter,
EActionComboBox *combo_box)
{
- GtkRadioAction *action;
- gchar *icon_name;
- gchar *stock_id;
- gboolean sensitive;
- gboolean visible;
+ EUIAction *action;
gint width;
gtk_tree_model_get (model, iter, COLUMN_ACTION, &action, -1);
@@ -102,14 +111,6 @@ action_combo_box_render_pixbuf (GtkCellLayout *layout,
if (action == NULL)
return;
- g_object_get (
- G_OBJECT (action),
- "icon-name", &icon_name,
- "sensitive", &sensitive,
- "stock-id", &stock_id,
- "visible", &visible,
- NULL);
-
/* If some action has an icon */
if (combo_box->priv->group_has_icons)
/* Keep the pixbuf renderer a fixed size for proper alignment. */
@@ -117,34 +118,16 @@ action_combo_box_render_pixbuf (GtkCellLayout *layout,
else
width = 0;
- /* We can't set both "icon-name" and "stock-id" because setting
- * one unsets the other. So pick the one that has a non-NULL
- * value. If both are non-NULL, "stock-id" wins. */
-
- if (stock_id != NULL)
- g_object_set (
- G_OBJECT (renderer),
- "sensitive", sensitive,
- "icon-name", NULL,
- "stock-id", stock_id,
- "stock-size", GTK_ICON_SIZE_MENU,
- "visible", visible,
- "width", width,
- NULL);
- else
- g_object_set (
- G_OBJECT (renderer),
- "sensitive", sensitive,
- "icon-name", icon_name,
- "stock-id", NULL,
- "stock-size", GTK_ICON_SIZE_MENU,
- "visible", visible,
- "width", width,
- NULL);
+ g_object_set (
+ G_OBJECT (renderer),
+ "sensitive", e_ui_action_get_sensitive (action),
+ "icon-name", e_ui_action_get_icon_name (action),
+ "stock-size", GTK_ICON_SIZE_MENU,
+ "visible", e_ui_action_is_visible (action),
+ "width", width,
+ NULL);
g_object_unref (action);
- g_free (icon_name);
- g_free (stock_id);
}
static void
@@ -154,11 +137,9 @@ action_combo_box_render_text (GtkCellLayout *layout,
GtkTreeIter *iter,
EActionComboBox *combo_box)
{
- GtkRadioAction *action;
- gchar **strv;
- gchar *label;
- gboolean sensitive;
- gboolean visible;
+ EUIAction *action;
+ const gchar *const_label;
+ gchar *label = NULL;
gint xpad;
gtk_tree_model_get (model, iter, COLUMN_ACTION, &action, -1);
@@ -167,26 +148,30 @@ action_combo_box_render_text (GtkCellLayout *layout,
if (action == NULL)
return;
- g_object_get (
- G_OBJECT (action),
- "label", &label,
- "sensitive", &sensitive,
- "visible", &visible,
- NULL);
+ const_label = e_ui_action_get_label (action);
+ if (const_label && strchr (const_label, '_')) {
+ guint ii, wr = 0;
- /* Strip out underscores. */
- strv = g_strsplit (label, "_", -1);
- g_free (label);
- label = g_strjoinv (NULL, strv);
- g_strfreev (strv);
+ /* Strip out underscores. */
+ label = g_strdup (const_label);
+ for (ii = 0; label[ii]; ii++) {
+ if (label[ii] != '_') {
+ if (wr != ii)
+ label[wr] = label[ii];
+ wr++;
+ }
+ }
+ if (ii != wr)
+ label[wr] = '\0';
+ }
xpad = combo_box->priv->group_has_icons ? 3 : 0;
g_object_set (
G_OBJECT (renderer),
- "sensitive", sensitive,
- "text", label,
- "visible", visible,
+ "sensitive", e_ui_action_get_sensitive (action),
+ "text", label ? label : const_label,
+ "visible", e_ui_action_is_visible (action),
"xpad", xpad,
NULL);
@@ -198,14 +183,13 @@ static gboolean
action_combo_box_is_row_separator (GtkTreeModel *model,
GtkTreeIter *iter)
{
- GtkAction *action;
+ EUIAction *action;
gboolean separator;
/* NULL actions are rendered as separators. */
gtk_tree_model_get (model, iter, COLUMN_ACTION, &action, -1);
separator = (action == NULL);
- if (action != NULL)
- g_object_unref (action);
+ g_clear_object (&action);
return separator;
}
@@ -214,7 +198,8 @@ static void
action_combo_box_update_model (EActionComboBox *combo_box)
{
GtkListStore *list_store;
- GSList *list;
+ GPtrArray *radio_group;
+ guint ii;
g_hash_table_remove_all (combo_box->priv->index);
@@ -227,45 +212,34 @@ action_combo_box_update_model (EActionComboBox *combo_box)
* insert separators in between consecutive integer values and
* still maintain the proper ordering. */
list_store = gtk_list_store_new (
- 2, GTK_TYPE_RADIO_ACTION, G_TYPE_FLOAT);
+ 2, E_TYPE_UI_ACTION, G_TYPE_FLOAT);
- list = gtk_radio_action_get_group (combo_box->priv->action);
+ radio_group = e_ui_action_get_radio_group (combo_box->priv->action);
combo_box->priv->group_has_icons = FALSE;
- while (list != NULL) {
+ for (ii = 0; radio_group && ii < radio_group->len; ii++) {
+ EUIAction *action = g_ptr_array_index (radio_group, ii);
GtkTreeRowReference *reference;
- GtkRadioAction *action = list->data;
GtkTreePath *path;
GtkTreeIter iter;
- gchar *icon_name = NULL;
- gchar *stock_id = NULL;
- gboolean visible = FALSE;
- gint value;
+ GVariant *target;
+ gint value = G_MININT32;
- g_object_get (action,
- "icon-name", &icon_name,
- "stock-id", &stock_id,
- "visible", &visible,
- NULL);
-
- if (!visible) {
- g_free (icon_name);
- g_free (stock_id);
-
- list = g_slist_next (list);
+ if (!e_ui_action_get_visible (action))
continue;
- }
- combo_box->priv->group_has_icons |=
- (icon_name != NULL || stock_id != NULL);
- g_free (icon_name);
- g_free (stock_id);
+ combo_box->priv->group_has_icons |= e_ui_action_get_icon_name (action) != NULL;
+
+ target = e_ui_action_ref_target (action);
+ if (target && g_variant_is_of_type (target, G_VARIANT_TYPE_INT32))
+ value = g_variant_get_int32 (target);
+ g_clear_pointer (&target, g_variant_unref);
gtk_list_store_append (list_store, &iter);
- g_object_get (action, "value", &value, NULL);
- gtk_list_store_set (
- list_store, &iter, COLUMN_ACTION,
- list->data, COLUMN_SORT, (gfloat) value, -1);
+ gtk_list_store_set (list_store, &iter,
+ COLUMN_ACTION, action,
+ COLUMN_SORT, (gfloat) value,
+ -1);
path = gtk_tree_model_get_path (
GTK_TREE_MODEL (list_store), &iter);
@@ -275,8 +249,6 @@ action_combo_box_update_model (EActionComboBox *combo_box)
combo_box->priv->index,
GINT_TO_POINTER (value), reference);
gtk_tree_path_free (path);
-
- list = g_slist_next (list);
}
gtk_tree_sortable_set_sort_column_id (
@@ -286,10 +258,7 @@ action_combo_box_update_model (EActionComboBox *combo_box)
GTK_COMBO_BOX (combo_box), GTK_TREE_MODEL (list_store));
g_object_unref (list_store);
- action_combo_box_action_changed_cb (
- combo_box->priv->action,
- combo_box->priv->action,
- combo_box);
+ action_combo_box_action_notify_state_cb (combo_box->priv->action, NULL, combo_box);
}
static void
@@ -316,6 +285,11 @@ action_combo_box_set_property (GObject *object,
E_ACTION_COMBO_BOX (object),
g_value_get_object (value));
return;
+ case PROP_CURRENT_VALUE:
+ e_action_combo_box_set_current_value (
+ E_ACTION_COMBO_BOX (object),
+ g_value_get_int (value));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -333,6 +307,11 @@ action_combo_box_get_property (GObject *object,
value, e_action_combo_box_get_action (
E_ACTION_COMBO_BOX (object)));
return;
+ case PROP_CURRENT_VALUE:
+ g_value_set_int (
+ value, e_action_combo_box_get_current_value (
+ E_ACTION_COMBO_BOX (object)));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -343,6 +322,8 @@ action_combo_box_dispose (GObject *object)
{
EActionComboBox *self = E_ACTION_COMBO_BOX (object);
+ e_action_combo_box_set_action (self, NULL);
+
g_clear_object (&self->priv->action);
g_clear_object (&self->priv->action_group);
g_hash_table_remove_all (self->priv->index);
@@ -400,10 +381,9 @@ action_combo_box_constructed (GObject *object)
static void
action_combo_box_changed (GtkComboBox *combo_box)
{
- GtkRadioAction *action;
+ EUIAction *action;
GtkTreeModel *model;
GtkTreeIter iter;
- gint value;
/* This method is virtual, so no need to chain up. */
@@ -412,8 +392,8 @@ action_combo_box_changed (GtkComboBox *combo_box)
model = gtk_combo_box_get_model (combo_box);
gtk_tree_model_get (model, &iter, COLUMN_ACTION, &action, -1);
- g_object_get (action, "value", &value, NULL);
- gtk_radio_action_set_current_value (action, value);
+ if (action != NULL)
+ e_ui_action_set_active (action, TRUE);
g_object_unref (action);
}
@@ -443,8 +423,16 @@ e_action_combo_box_class_init (EActionComboBoxClass *class)
g_param_spec_object (
"action",
"Action",
- "A GtkRadioAction",
- GTK_TYPE_RADIO_ACTION,
+ "An EUIAction",
+ E_TYPE_UI_ACTION,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_CURRENT_VALUE,
+ g_param_spec_int (
+ "current-value", NULL, NULL,
+ G_MININT32, G_MAXINT32, 0,
G_PARAM_READWRITE));
}
@@ -466,12 +454,12 @@ e_action_combo_box_new (void)
}
GtkWidget *
-e_action_combo_box_new_with_action (GtkRadioAction *action)
+e_action_combo_box_new_with_action (EUIAction *action)
{
return g_object_new (E_TYPE_ACTION_COMBO_BOX, "action", action, NULL);
}
-GtkRadioAction *
+EUIAction *
e_action_combo_box_get_action (EActionComboBox *combo_box)
{
g_return_val_if_fail (E_IS_ACTION_COMBO_BOX (combo_box), NULL);
@@ -481,46 +469,60 @@ e_action_combo_box_get_action (EActionComboBox *combo_box)
void
e_action_combo_box_set_action (EActionComboBox *combo_box,
- GtkRadioAction *action)
+ EUIAction *action)
{
+ gboolean last_state_set = FALSE;
+ gint last_state;
+
g_return_if_fail (E_IS_ACTION_COMBO_BOX (combo_box));
if (action != NULL)
- g_return_if_fail (GTK_IS_RADIO_ACTION (action));
+ g_return_if_fail (E_IS_UI_ACTION (action));
- if (combo_box->priv->action != NULL) {
- g_signal_handler_disconnect (
- combo_box->priv->action,
- combo_box->priv->changed_handler_id);
- g_object_unref (combo_box->priv->action);
+ if (action == combo_box->priv->action)
+ return;
+
+ if (combo_box->priv->action) {
+ last_state_set = TRUE;
+ last_state = e_action_combo_box_get_current_value (combo_box);
}
- if (combo_box->priv->action_group != NULL) {
+ if (combo_box->priv->action_group) {
g_signal_handler_disconnect (
combo_box->priv->action_group,
combo_box->priv->group_sensitive_handler_id);
g_signal_handler_disconnect (
combo_box->priv->action_group,
combo_box->priv->group_visible_handler_id);
- g_object_unref (combo_box->priv->action_group);
- combo_box->priv->action_group = NULL;
+ g_clear_object (&combo_box->priv->action_group);
+ combo_box->priv->group_visible_handler_id = 0;
+ combo_box->priv->group_sensitive_handler_id = 0;
}
if (action != NULL) {
- /* This also adds a reference to the combo_box->priv->action_group */
- g_object_get (
- g_object_ref (action), "action-group",
- &combo_box->priv->action_group, NULL);
+ g_object_ref (action);
+ combo_box->priv->action_group = e_ui_action_get_action_group (action);
+
+ if (combo_box->priv->action_group)
+ g_object_ref (combo_box->priv->action_group);
+ }
+
+ if (combo_box->priv->action != NULL) {
+ g_signal_handler_disconnect (
+ combo_box->priv->action,
+ combo_box->priv->notify_state_handler_id);
+ g_clear_object (&combo_box->priv->action);
+ combo_box->priv->notify_state_handler_id = 0;
}
combo_box->priv->action = action;
action_combo_box_update_model (combo_box);
- if (combo_box->priv->action != NULL)
- combo_box->priv->changed_handler_id = g_signal_connect (
- combo_box->priv->action, "changed",
- G_CALLBACK (action_combo_box_action_changed_cb),
- combo_box);
+ if (combo_box->priv->action != NULL) {
+ combo_box->priv->notify_state_handler_id = g_signal_connect (
+ combo_box->priv->action, "notify::state",
+ G_CALLBACK (action_combo_box_action_notify_state_cb), combo_box);
+ }
if (combo_box->priv->action_group != NULL) {
combo_box->priv->group_sensitive_handler_id =
@@ -537,16 +539,33 @@ e_action_combo_box_set_action (EActionComboBox *combo_box,
combo_box);
}
+ if (action && last_state_set && g_hash_table_contains (combo_box->priv->index, GINT_TO_POINTER (last_state)))
+ e_action_combo_box_set_current_value (combo_box, last_state);
+
g_object_notify (G_OBJECT (combo_box), "action");
}
gint
e_action_combo_box_get_current_value (EActionComboBox *combo_box)
{
+ GVariant *state;
+ gint value = 0;
+
g_return_val_if_fail (E_IS_ACTION_COMBO_BOX (combo_box), 0);
g_return_val_if_fail (combo_box->priv->action != NULL, 0);
- return gtk_radio_action_get_current_value (combo_box->priv->action);
+ state = g_action_get_state (G_ACTION (combo_box->priv->action));
+ if (state && g_variant_is_of_type (state, G_VARIANT_TYPE_INT32)) {
+ value = g_variant_get_int32 (state);
+ } else if (state) {
+ g_warning ("%s: Action '%s' does not hold int32 state", G_STRFUNC, g_action_get_name (G_ACTION (combo_box->priv->action)));
+ } else {
+ g_warning ("%s: Action '%s' does not have state", G_STRFUNC, g_action_get_name (G_ACTION (combo_box->priv->action)));
+ }
+
+ g_clear_pointer (&state, g_variant_unref);
+
+ return value;
}
void
@@ -556,8 +575,12 @@ e_action_combo_box_set_current_value (EActionComboBox *combo_box,
g_return_if_fail (E_IS_ACTION_COMBO_BOX (combo_box));
g_return_if_fail (combo_box->priv->action != NULL);
- gtk_radio_action_set_current_value (
- combo_box->priv->action, current_value);
+ if (current_value == e_action_combo_box_get_current_value (combo_box))
+ return;
+
+ e_ui_action_set_state (combo_box->priv->action, g_variant_new_int32 (current_value));
+
+ g_object_notify (G_OBJECT (combo_box), "current-value");
}
void
diff --git a/src/e-util/e-action-combo-box.h b/src/e-util/e-action-combo-box.h
index 7ec81cda80..30131e33d6 100644
--- a/src/e-util/e-action-combo-box.h
+++ b/src/e-util/e-action-combo-box.h
@@ -23,11 +23,12 @@
#ifndef E_ACTION_COMBO_BOX_H
#define E_ACTION_COMBO_BOX_H
-/* This is a GtkComboBox that is driven by a group of GtkRadioActions.
- * Just plug in a GtkRadioAction and the widget will handle the rest.
+/* This is a GtkComboBox that is driven by a group of an EUIAction.
+ * Just plug in an EUIAction and the widget will handle the rest.
* (Based on GtkhtmlComboBox.) */
#include
+#include
/* Standard GObject macros */
#define E_TYPE_ACTION_COMBO_BOX \
@@ -66,10 +67,10 @@ struct _EActionComboBoxClass {
GType e_action_combo_box_get_type (void) G_GNUC_CONST;
GtkWidget * e_action_combo_box_new (void);
GtkWidget * e_action_combo_box_new_with_action
- (GtkRadioAction *action);
-GtkRadioAction *e_action_combo_box_get_action (EActionComboBox *combo_box);
+ (EUIAction *action);
+EUIAction * e_action_combo_box_get_action (EActionComboBox *combo_box);
void e_action_combo_box_set_action (EActionComboBox *combo_box,
- GtkRadioAction *action);
+ EUIAction *action);
gint e_action_combo_box_get_current_value
(EActionComboBox *combo_box);
void e_action_combo_box_set_current_value
diff --git a/src/e-util/e-alert-bar.c b/src/e-util/e-alert-bar.c
index ba94050036..e57cafe376 100644
--- a/src/e-util/e-alert-bar.c
+++ b/src/e-util/e-alert-bar.c
@@ -170,7 +170,7 @@ alert_bar_show_alert (EAlertBar *alert_bar)
/* Add alert-specific buttons. */
link = e_alert_peek_actions (alert);
while (link != NULL) {
- GtkAction *action = GTK_ACTION (link->data);
+ EUIAction *action = E_UI_ACTION (link->data);
/* These actions are already wired to trigger an
* EAlert::response signal when activated, which
@@ -178,13 +178,10 @@ alert_bar_show_alert (EAlertBar *alert_bar)
* we can add buttons directly to the action
* area without knowning their response IDs. */
- widget = gtk_button_new ();
+ widget = e_alert_create_button_for_action (action);
- gtk_activatable_set_related_action (GTK_ACTIVATABLE (widget), action);
gtk_box_pack_end (GTK_BOX (action_area), widget, FALSE, FALSE, 0);
- e_alert_update_destructive_action_style (action, widget);
-
link = g_list_next (link);
}
diff --git a/src/e-util/e-alert-dialog.c b/src/e-util/e-alert-dialog.c
index 6dba243fdd..4901b52686 100644
--- a/src/e-util/e-alert-dialog.c
+++ b/src/e-util/e-alert-dialog.c
@@ -144,12 +144,12 @@ alert_dialog_constructed (GObject *object)
/* Add buttons from actions. */
link = e_alert_peek_actions (alert);
if (!link && !e_alert_peek_widgets (alert)) {
- GtkAction *action;
+ EUIAction *action;
/* Make sure there is at least one action,
* thus the dialog can be closed. */
- action = gtk_action_new (
- "alert-response-0", _("_Dismiss"), NULL, NULL);
+ action = e_ui_action_new ("alert-dialog-map", "alert-response-0", NULL);
+ e_ui_action_set_label (action, _("_Dismiss"));
e_alert_add_action (alert, action, GTK_RESPONSE_CLOSE, FALSE);
g_object_unref (action);
@@ -158,7 +158,7 @@ alert_dialog_constructed (GObject *object)
while (link != NULL) {
GtkWidget *button;
- GtkAction *action = GTK_ACTION (link->data);
+ EUIAction *action = E_UI_ACTION (link->data);
gpointer data;
/* These actions are already wired to trigger an
@@ -168,14 +168,11 @@ alert_dialog_constructed (GObject *object)
* area without knowing their response IDs.
* (XXX Well, kind of. See below.) */
- button = gtk_button_new ();
+ button = e_alert_create_button_for_action (action);
gtk_widget_set_can_default (button, TRUE);
- gtk_activatable_set_related_action (GTK_ACTIVATABLE (button), action);
gtk_box_pack_end (GTK_BOX (action_area), button, FALSE, FALSE, 0);
- e_alert_update_destructive_action_style (action, button);
-
/* This is set in e_alert_add_action(). */
data = g_object_get_data (G_OBJECT (action), "e-alert-response-id");
diff --git a/src/e-util/e-alert.c b/src/e-util/e-alert.c
index de78938f76..ccda2b1fd2 100644
--- a/src/e-util/e-alert.c
+++ b/src/e-util/e-alert.c
@@ -34,8 +34,10 @@
#include
-#include "e-alert.h"
#include "e-alert-sink.h"
+#include "e-ui-action.h"
+
+#include "e-alert.h"
#define d(x)
@@ -91,8 +93,8 @@ struct _EAlertPrivate {
gint default_response;
guint timeout_id;
- /* It may occur to one that we could use a GtkActionGroup here,
- * but we need to preserve the button order and GtkActionGroup
+ /* It may occur to one that we could use a EUIActionGroup here,
+ * but we need to preserve the button order and EUIActionGroup
* uses a hash table, which does not preserve order. */
GQueue actions;
@@ -373,7 +375,8 @@ e_alert_load_tables (void)
static void
alert_action_activate (EAlert *alert,
- GtkAction *action)
+ GVariant *parameter,
+ EUIAction *action)
{
GObject *object;
gpointer data;
@@ -552,7 +555,7 @@ alert_dispose (GObject *object)
}
while (!g_queue_is_empty (&alert->priv->actions)) {
- GtkAction *action;
+ EUIAction *action;
action = g_queue_pop_head (&alert->priv->actions);
g_signal_handlers_disconnect_by_func (
@@ -604,23 +607,21 @@ alert_constructed (GObject *object)
/* Build actions out of the button definitions. */
button = definition->buttons;
while (button != NULL) {
- GtkAction *action;
+ EUIAction *action;
gchar *action_name;
action_name = g_strdup_printf ("alert-response-%d", ii++);
if (button->stock_id != NULL) {
- action = gtk_action_new (
- action_name, NULL, NULL, button->stock_id);
- e_alert_add_action (
- alert, action, button->response_id, button->destructive);
+ action = e_ui_action_new ("alert-map", action_name, NULL);
+ e_ui_action_set_icon_name (action, button->stock_id);
+ e_alert_add_action (alert, action, button->response_id, button->destructive);
g_object_unref (action);
} else if (button->label != NULL) {
- action = gtk_action_new (
- action_name, button->label, NULL, NULL);
- e_alert_add_action (
- alert, action, button->response_id, button->destructive);
+ action = e_ui_action_new ("alert-map", action_name, NULL);
+ e_ui_action_set_label (action, button->label);
+ e_alert_add_action (alert, action, button->response_id, button->destructive);
g_object_unref (action);
}
@@ -937,12 +938,12 @@ e_alert_get_icon_name (EAlert *alert)
void
e_alert_add_action (EAlert *alert,
- GtkAction *action,
+ EUIAction *action,
gint response_id,
gboolean is_destructive)
{
g_return_if_fail (E_IS_ALERT (alert));
- g_return_if_fail (GTK_IS_ACTION (action));
+ g_return_if_fail (E_IS_UI_ACTION (action));
g_object_set_data (
G_OBJECT (action), "e-alert-response-id",
@@ -958,7 +959,7 @@ e_alert_add_action (EAlert *alert,
g_queue_push_tail (&alert->priv->actions, g_object_ref (action));
}
-GList *
+GList * /* EUIAction * */
e_alert_peek_actions (EAlert *alert)
{
g_return_val_if_fail (E_IS_ALERT (alert), NULL);
@@ -1063,19 +1064,48 @@ e_alert_submit_valist (EAlertSink *alert_sink,
g_object_unref (alert);
}
-void
-e_alert_update_destructive_action_style (GtkAction *for_action,
- GtkWidget *button)
+static void
+alert_action_button_clicked_cb (GtkButton *button,
+ gpointer user_data)
{
+ EUIAction *action = user_data;
+
+ g_action_activate (G_ACTION (action), NULL);
+}
+
+GtkWidget *
+e_alert_create_button_for_action (EUIAction *for_action)
+{
+ GtkWidget *widget;
GtkStyleContext *style_context;
- g_return_if_fail (GTK_IS_ACTION (for_action));
- g_return_if_fail (GTK_IS_WIDGET (button));
+ g_return_val_if_fail (E_IS_UI_ACTION (for_action), NULL);
- style_context = gtk_widget_get_style_context (button);
+ /* action's icon-name is stock_id */
+ if (e_ui_action_get_icon_name (for_action)) {
+ widget = gtk_button_new_from_stock (e_ui_action_get_icon_name (for_action));
+ if (e_ui_action_get_label (for_action)) {
+ gtk_button_set_use_underline (GTK_BUTTON (widget), TRUE);
+ gtk_button_set_label (GTK_BUTTON (widget), e_ui_action_get_label (for_action));
+ }
+ } else {
+ widget = gtk_button_new_with_mnemonic (e_ui_action_get_label (for_action));
+ }
+
+ if (e_ui_action_get_tooltip (for_action))
+ gtk_widget_set_tooltip_text (widget, e_ui_action_get_tooltip (for_action));
+
+ gtk_widget_set_visible (widget, TRUE);
+
+ g_signal_connect_object (widget, "clicked",
+ G_CALLBACK (alert_action_button_clicked_cb), for_action, 0);
+
+ style_context = gtk_widget_get_style_context (widget);
if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (for_action), "e-alert-is-destructive")) != 0)
gtk_style_context_add_class (style_context, "destructive-action");
else
gtk_style_context_remove_class (style_context, "destructive-action");
+
+ return widget;
}
diff --git a/src/e-util/e-alert.h b/src/e-util/e-alert.h
index 6463f03cb9..46af78dcad 100644
--- a/src/e-util/e-alert.h
+++ b/src/e-util/e-alert.h
@@ -31,6 +31,8 @@
#include
#include
+#include
+
/* Standard GObject macros */
#define E_TYPE_ALERT \
(e_alert_get_type ())
@@ -101,10 +103,10 @@ void e_alert_set_secondary_text (EAlert *alert,
const gchar *secondary_text);
const gchar * e_alert_get_icon_name (EAlert *alert);
void e_alert_add_action (EAlert *alert,
- GtkAction *action,
+ EUIAction *action,
gint response_id,
gboolean is_destructive);
-GList * e_alert_peek_actions (EAlert *alert);
+GList * e_alert_peek_actions (EAlert *alert); /* EUIAction * */
void e_alert_add_widget (EAlert *alert,
GtkWidget *widget);
GList * e_alert_peek_widgets (EAlert *alert);
@@ -121,9 +123,7 @@ void e_alert_submit (struct _EAlertSink *alert_sink,
void e_alert_submit_valist (struct _EAlertSink *alert_sink,
const gchar *tag,
va_list va);
-void e_alert_update_destructive_action_style
- (GtkAction *for_action,
- GtkWidget *button);
+GtkWidget * e_alert_create_button_for_action(EUIAction *for_action);
G_END_DECLS
diff --git a/src/e-util/e-attachment-bar.c b/src/e-util/e-attachment-bar.c
index 3d86bf7bcd..ccf9cf5f55 100644
--- a/src/e-util/e-attachment-bar.c
+++ b/src/e-util/e-attachment-bar.c
@@ -76,8 +76,7 @@ static void
attachment_bar_update_status (EAttachmentBar *bar)
{
EAttachmentStore *store;
- GtkActivatable *activatable;
- GtkAction *action;
+ EUIAction *action;
GtkLabel *label;
gint num_attachments;
guint64 total_size;
@@ -107,13 +106,11 @@ attachment_bar_update_status (EAttachmentBar *bar)
gtk_label_set_markup (label, markup);
g_free (markup);
- activatable = GTK_ACTIVATABLE (bar->priv->save_all_button);
- action = gtk_activatable_get_related_action (activatable);
- gtk_action_set_visible (action, num_attachments > 1);
+ action = e_attachment_view_get_action (E_ATTACHMENT_VIEW (bar), "save-all");
+ e_ui_action_set_visible (action, num_attachments > 1);
- activatable = GTK_ACTIVATABLE (bar->priv->save_one_button);
- action = gtk_activatable_get_related_action (activatable);
- gtk_action_set_visible (action, (num_attachments == 1));
+ action = e_attachment_view_get_action (E_ATTACHMENT_VIEW (bar), "save-one");
+ e_ui_action_set_visible (action, (num_attachments == 1));
g_free (display_size);
}
@@ -667,7 +664,8 @@ e_attachment_bar_init (EAttachmentBar *bar)
GtkSizeGroup *size_group;
GtkWidget *container;
GtkWidget *widget;
- GtkAction *action;
+ EUIAction *action;
+ EUIManager *ui_manager;
GtkAdjustment *adjustment;
gtk_widget_set_name (GTK_WIDGET (bar), "e-attachment-bar");
@@ -738,7 +736,7 @@ e_attachment_bar_init (EAttachmentBar *bar)
container = GTK_WIDGET (bar);
- widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_paned_pack1 (GTK_PANED (container), widget, TRUE, FALSE);
bar->priv->content_area = g_object_ref (widget);
gtk_widget_show (widget);
@@ -762,25 +760,33 @@ e_attachment_bar_init (EAttachmentBar *bar)
/* The "Save All" button proxies the "save-all" action from
* one of the two attachment views. Doesn't matter which. */
- widget = gtk_button_new ();
view = E_ATTACHMENT_VIEW (bar->priv->icon_view);
action = e_attachment_view_get_action (view, "save-all");
- gtk_button_set_image (GTK_BUTTON (widget), gtk_image_new ());
- gtk_activatable_set_related_action (GTK_ACTIVATABLE (widget), action);
+
+ widget = gtk_button_new_with_mnemonic (e_ui_action_get_label (action));
+ if (e_ui_action_get_icon_name (action))
+ gtk_button_set_image (GTK_BUTTON (widget), gtk_image_new_from_icon_name (e_ui_action_get_icon_name (action), GTK_ICON_SIZE_BUTTON));
+ e_ui_action_util_assign_to_widget (action, widget);
gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
bar->priv->save_all_button = g_object_ref (widget);
gtk_widget_show (widget);
/* Same deal with the "Save" button. */
- widget = gtk_button_new ();
view = E_ATTACHMENT_VIEW (bar->priv->icon_view);
action = e_attachment_view_get_action (view, "save-one");
- gtk_button_set_image (GTK_BUTTON (widget), gtk_image_new ());
- gtk_activatable_set_related_action (GTK_ACTIVATABLE (widget), action);
+
+ widget = gtk_button_new_with_mnemonic (e_ui_action_get_label (action));
+ if (e_ui_action_get_icon_name (action))
+ gtk_button_set_image (GTK_BUTTON (widget), gtk_image_new_from_icon_name (e_ui_action_get_icon_name (action), GTK_ICON_SIZE_BUTTON));
+ e_ui_action_util_assign_to_widget (action, widget);
gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
bar->priv->save_one_button = g_object_ref (widget);
gtk_widget_show (widget);
+ /* needed to be able to click the buttons */
+ ui_manager = e_attachment_view_get_ui_manager (view);
+ e_ui_manager_add_action_groups_to_widget (ui_manager, container);
+
widget = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
gtk_widget_show (widget);
diff --git a/src/e-util/e-attachment-handler-image.c b/src/e-util/e-attachment-handler-image.c
index 4d7081e588..93f4529318 100644
--- a/src/e-util/e-attachment-handler-image.c
+++ b/src/e-util/e-attachment-handler-image.c
@@ -31,15 +31,6 @@ struct _EAttachmentHandlerImagePrivate {
gint placeholder;
};
-static const gchar *ui =
-""
-" "
-" "
-" "
-" "
-" "
-"";
-
G_DEFINE_TYPE_WITH_PRIVATE (EAttachmentHandlerImage, e_attachment_handler_image, E_TYPE_ATTACHMENT_HANDLER)
static void
@@ -102,9 +93,11 @@ exit:
}
static void
-action_image_set_as_background_cb (GtkAction *action,
- EAttachmentHandler *handler)
+action_image_set_as_background_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EAttachmentHandler *handler = user_data;
EAttachmentView *view;
EAttachment *attachment;
GFile *destination;
@@ -132,22 +125,12 @@ action_image_set_as_background_cb (GtkAction *action,
g_list_free (selected);
}
-static GtkActionEntry standard_entries[] = {
-
- { "image-set-as-background",
- NULL,
- N_("Set as _Background"),
- NULL,
- NULL, /* XXX Add a tooltip! */
- G_CALLBACK (action_image_set_as_background_cb) }
-};
-
static void
attachment_handler_image_update_actions_cb (EAttachmentView *view,
EAttachmentHandler *handler)
{
EAttachment *attachment;
- GtkActionGroup *action_group;
+ EUIActionGroup *action_group;
gchar *mime_type;
GList *selected;
gboolean visible = FALSE;
@@ -173,7 +156,7 @@ attachment_handler_image_update_actions_cb (EAttachmentView *view,
exit:
action_group = e_attachment_view_get_action_group (view, "image");
- gtk_action_group_set_visible (action_group, visible);
+ e_ui_action_group_set_visible (action_group, visible);
g_list_foreach (selected, (GFunc) g_object_unref, NULL);
g_list_free (selected);
@@ -182,11 +165,28 @@ exit:
static void
attachment_handler_image_constructed (GObject *object)
{
+ static const gchar *eui =
+ ""
+ ""
+ ""
+ " "
+ ""
+ ""
+ "";
+
+ static const EUIActionEntry standard_entries[] = {
+
+ { "image-set-as-background",
+ NULL,
+ N_("Set as _Background"),
+ NULL,
+ NULL,
+ action_image_set_as_background_cb, NULL, NULL, NULL }
+ };
+
EAttachmentHandler *handler;
EAttachmentView *view;
- GtkActionGroup *action_group;
- GtkUIManager *ui_manager;
- GError *error = NULL;
+ EUIManager *ui_manager;
handler = E_ATTACHMENT_HANDLER (object);
@@ -194,19 +194,10 @@ attachment_handler_image_constructed (GObject *object)
G_OBJECT_CLASS (e_attachment_handler_image_parent_class)->constructed (object);
view = e_attachment_handler_get_view (handler);
-
- action_group = e_attachment_view_add_action_group (view, "image");
- gtk_action_group_add_actions (
- action_group, standard_entries,
- G_N_ELEMENTS (standard_entries), object);
-
ui_manager = e_attachment_view_get_ui_manager (view);
- gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
- if (error != NULL) {
- g_warning ("%s", error->message);
- g_error_free (error);
- }
+ e_ui_manager_add_actions_with_eui_data (ui_manager, "image", NULL,
+ standard_entries, G_N_ELEMENTS (standard_entries), handler, eui);
g_signal_connect (
view, "update-actions",
diff --git a/src/e-util/e-attachment-paned.c b/src/e-util/e-attachment-paned.c
index 0d5f05e8ec..6a842441bf 100644
--- a/src/e-util/e-attachment-paned.c
+++ b/src/e-util/e-attachment-paned.c
@@ -553,7 +553,8 @@ e_attachment_paned_init (EAttachmentPaned *paned)
GtkSizeGroup *size_group;
GtkWidget *container;
GtkWidget *widget;
- GtkAction *action;
+ EUIAction *action;
+ EUIManager *ui_manager;
paned->priv = e_attachment_paned_get_instance_private (paned);
paned->priv->model = e_attachment_store_new ();
@@ -650,14 +651,20 @@ e_attachment_paned_init (EAttachmentPaned *paned)
/* The "Add Attachment" button proxies the "add" action from
* one of the two attachment views. Doesn't matter which. */
- widget = gtk_button_new ();
view = E_ATTACHMENT_VIEW (paned->priv->icon_view);
action = e_attachment_view_get_action (view, "add");
- gtk_button_set_image (GTK_BUTTON (widget), gtk_image_new ());
- gtk_activatable_set_related_action (GTK_ACTIVATABLE (widget), action);
+
+ widget = gtk_button_new_with_mnemonic (e_ui_action_get_label (action));
+ if (e_ui_action_get_icon_name (action))
+ gtk_button_set_image (GTK_BUTTON (widget), gtk_image_new_from_icon_name (e_ui_action_get_icon_name (action), GTK_ICON_SIZE_BUTTON));
+ e_ui_action_util_assign_to_widget (action, widget);
gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
gtk_widget_show (widget);
+ /* needed to be able to click the button */
+ ui_manager = e_attachment_view_get_ui_manager (view);
+ e_ui_manager_add_action_groups_to_widget (ui_manager, container);
+
widget = gtk_combo_box_text_new ();
gtk_size_group_add_widget (size_group, widget);
gtk_combo_box_text_append_text (
diff --git a/src/e-util/e-attachment-view.c b/src/e-util/e-attachment-view.c
index 5565ce6133..2c9e1f9f59 100644
--- a/src/e-util/e-attachment-view.c
+++ b/src/e-util/e-attachment-view.c
@@ -42,27 +42,6 @@ static GtkTargetEntry target_table[] = {
{ (gchar *) "_NETSCAPE_URL", 0, 0 }
};
-static const gchar *ui =
-""
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-"";
-
static gulong signals[LAST_SIGNAL];
G_DEFINE_INTERFACE (
@@ -86,9 +65,11 @@ call_attachment_load_handle_error (GObject *source_object,
}
static void
-action_add_cb (GtkAction *action,
- EAttachmentView *view)
+action_add_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EAttachmentView *view = user_data;
EAttachmentStore *store;
gpointer parent;
@@ -163,9 +144,11 @@ attachment_popover_popup (EAttachmentView *view,
}
static void
-action_add_uri_cb (GtkAction *action,
- EAttachmentView *view)
+action_add_uri_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EAttachmentView *view = user_data;
EAttachmentPopover *popover;
EAttachment *attachment;
GFileInfo *file_info;
@@ -197,9 +180,11 @@ action_add_uri_cb (GtkAction *action,
}
static void
-action_cancel_cb (GtkAction *action,
- EAttachmentView *view)
+action_cancel_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EAttachmentView *view = user_data;
EAttachment *attachment;
GList *list;
@@ -214,9 +199,11 @@ action_cancel_cb (GtkAction *action,
}
static void
-action_open_with_cb (GtkAction *action,
- EAttachmentView *view)
+action_open_with_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EAttachmentView *view = user_data;
EAttachment *attachment;
EAttachmentStore *store;
GtkWidget *dialog;
@@ -267,9 +254,12 @@ action_open_with_cb (GtkAction *action,
}
static void
-action_open_with_app_info_cb (GtkAction *action,
- EAttachmentView *view)
+action_open_with_app_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EAttachmentView *view = user_data;
+ EAttachmentViewPrivate *priv = e_attachment_view_get_private (view);
GAppInfo *app_info;
GtkTreePath *path;
GList *list;
@@ -278,18 +268,19 @@ action_open_with_app_info_cb (GtkAction *action,
g_return_if_fail (g_list_length (list) == 1);
path = list->data;
- app_info = g_object_get_data (G_OBJECT (action), "app-info");
+ app_info = g_hash_table_lookup (priv->open_with_apps_hash, GINT_TO_POINTER (g_variant_get_int32 (parameter)));
e_attachment_view_open_path (view, path, app_info);
- g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
- g_list_free (list);
+ g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free);
}
static void
-action_properties_cb (GtkAction *action,
- EAttachmentView *view)
+action_properties_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EAttachmentView *view = user_data;
EAttachment *attachment;
EAttachmentPopover *popover;
GList *list;
@@ -308,9 +299,11 @@ action_properties_cb (GtkAction *action,
}
static void
-action_reload_cb (GtkAction *action,
- EAttachmentView *view)
+action_reload_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EAttachmentView *view = user_data;
GList *list, *link;
gpointer parent;
@@ -342,9 +335,12 @@ action_reload_cb (GtkAction *action,
}
static void
-action_remove_cb (GtkAction *action,
- EAttachmentView *view)
+action_remove_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EAttachmentView *view = user_data;
+
e_attachment_view_remove_selected (view, FALSE);
}
@@ -364,9 +360,11 @@ call_attachment_save_handle_error (GObject *source_object,
}
static void
-action_save_all_cb (GtkAction *action,
- EAttachmentView *view)
+action_save_all_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EAttachmentView *view = user_data;
EAttachmentStore *store;
GList *list, *iter;
GFile *destination;
@@ -406,9 +404,11 @@ exit:
}
static void
-action_save_as_cb (GtkAction *action,
- EAttachmentView *view)
+action_save_as_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EAttachmentView *view = user_data;
EAttachmentStore *store;
GList *list, *iter;
GFile *destination;
@@ -444,84 +444,6 @@ exit:
g_list_free (list);
}
-static GtkActionEntry standard_entries[] = {
-
- { "cancel",
- "process-stop",
- N_("_Cancel"),
- NULL,
- NULL, /* XXX Add a tooltip! */
- G_CALLBACK (action_cancel_cb) },
-
- { "open-with",
- NULL,
- N_("Open With Other Application…"),
- NULL,
- NULL, /* XXX Add a tooltip! */
- G_CALLBACK (action_open_with_cb) },
-
- { "save-all",
- "document-save-as",
- N_("S_ave All"),
- NULL,
- NULL, /* XXX Add a tooltip! */
- G_CALLBACK (action_save_all_cb) },
-
- { "save-as",
- "document-save-as",
- N_("Sa_ve As"),
- NULL,
- NULL, /* XXX Add a tooltip! */
- G_CALLBACK (action_save_as_cb) },
-
- /* Alternate "save-all" label, for when
- * the attachment store has one row. */
- { "save-one",
- "document-save-as",
- N_("Save _As"),
- NULL,
- NULL, /* XXX Add a tooltip! */
- G_CALLBACK (action_save_all_cb) },
-};
-
-static GtkActionEntry editable_entries[] = {
-
- { "add",
- "list-add",
- N_("A_dd Attachment…"),
- NULL,
- N_("Attach a file"),
- G_CALLBACK (action_add_cb) },
-
- { "add-uri",
- "emblem-web",
- N_("Add _URI…"),
- NULL,
- N_("Attach a URI"),
- G_CALLBACK (action_add_uri_cb) },
-
- { "properties",
- "document-properties",
- N_("_Properties"),
- NULL,
- NULL, /* XXX Add a tooltip! */
- G_CALLBACK (action_properties_cb) },
-
- { "reload",
- "view-refresh",
- N_("Re_load"),
- NULL,
- N_("Reload attachment content"),
- G_CALLBACK (action_reload_cb) },
-
- { "remove",
- "list-remove",
- N_("_Remove"),
- NULL,
- NULL, /* XXX Add a tooltip! */
- G_CALLBACK (action_remove_cb) }
-};
-
static void
attachment_view_handle_uri_with_title (EAttachmentView *view,
GdkDragContext *drag_context,
@@ -882,15 +804,42 @@ attachment_view_text_x_moz_url (EAttachmentView *view,
g_free (uri_with_title);
}
+static gboolean
+e_attachment_view_ui_manager_create_item_cb (EUIManager *manager,
+ EUIElement *elem,
+ EUIAction *action,
+ EUIElementKind for_kind,
+ GObject **out_item,
+ gpointer user_data)
+{
+ EAttachmentView *self = user_data;
+ EAttachmentViewPrivate *priv;
+
+ g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (self), FALSE);
+
+ if (for_kind != E_UI_ELEMENT_KIND_MENU ||
+ g_strcmp0 (g_action_get_name (G_ACTION (action)), "EAttachmentView::open-with-app") != 0)
+ return FALSE;
+
+ priv = e_attachment_view_get_private (self);
+
+ if (priv->open_with_apps_menu)
+ *out_item = G_OBJECT (g_menu_item_new_section (NULL, G_MENU_MODEL (priv->open_with_apps_menu)));
+ else
+ *out_item = NULL;
+
+ return TRUE;
+}
+
static void
attachment_view_update_actions (EAttachmentView *view)
{
EAttachmentViewPrivate *priv;
EAttachment *attachment;
- GtkActionGroup *action_group;
- GtkAction *action;
+ EUIAction *action;
GList *list, *iter;
guint n_selected;
+ gint op_id = 0;
gboolean busy = FALSE;
gboolean may_reload = FALSE;
gboolean is_uri = FALSE;
@@ -930,32 +879,29 @@ attachment_view_update_actions (EAttachmentView *view)
g_list_free_full (list, g_object_unref);
action = e_attachment_view_get_action (view, "cancel");
- gtk_action_set_visible (action, busy);
+ e_ui_action_set_visible (action, busy);
action = e_attachment_view_get_action (view, "open-with");
- gtk_action_set_visible (action, !busy && n_selected == 1 && !e_util_is_running_flatpak ());
+ e_ui_action_set_visible (action, !busy && n_selected == 1 && !e_util_is_running_flatpak ());
action = e_attachment_view_get_action (view, "properties");
- gtk_action_set_visible (action, !busy && n_selected == 1);
+ e_ui_action_set_visible (action, !busy && n_selected == 1);
action = e_attachment_view_get_action (view, "reload");
- gtk_action_set_visible (action, may_reload && !is_uri);
- gtk_action_set_sensitive (action, !busy);
+ e_ui_action_set_visible (action, may_reload && !is_uri);
+ e_ui_action_set_sensitive (action, !busy);
action = e_attachment_view_get_action (view, "remove");
- gtk_action_set_visible (action, !busy && n_selected > 0);
+ e_ui_action_set_visible (action, !busy && n_selected > 0);
action = e_attachment_view_get_action (view, "save-as");
- gtk_action_set_visible (action, !busy && !is_uri && n_selected > 0);
+ e_ui_action_set_visible (action, !busy && !is_uri && n_selected > 0);
action = e_attachment_view_get_action (view, "add-uri");
- gtk_action_set_visible (action, priv->allow_uri);
+ e_ui_action_set_visible (action, priv->allow_uri);
- /* Clear out the "openwith" action group. */
- gtk_ui_manager_remove_ui (priv->ui_manager, priv->merge_id);
- action_group = e_attachment_view_get_action_group (view, "openwith");
- e_action_group_remove_all_actions (action_group);
- gtk_ui_manager_ensure_update (priv->ui_manager);
+ g_menu_remove_all (priv->open_with_apps_menu);
+ g_hash_table_remove_all (priv->open_with_apps_hash);
if (!attachment || busy) {
g_clear_object (&attachment);
@@ -969,12 +915,11 @@ attachment_view_update_actions (EAttachmentView *view)
for (iter = list; iter != NULL; iter = iter->next) {
GAppInfo *app_info = iter->data;
+ GMenuItem *menu_item;
GIcon *app_icon;
const gchar *app_id;
const gchar *app_name;
- gchar *action_tooltip;
- gchar *action_label;
- gchar *action_name;
+ gchar *label;
if (app_info) {
app_id = g_app_info_get_id (app_info);
@@ -993,47 +938,23 @@ attachment_view_update_actions (EAttachmentView *view)
if (g_str_equal (app_id, "org.gnome.Evolution.desktop"))
continue;
- action_name = g_strdup_printf ("open-with-%s", app_id);
+ if (app_info)
+ label = g_strdup_printf (_("Open With “%s”"), app_name);
+ else
+ label = g_strdup (_("Open With Default Application"));
- if (app_info) {
- action_label = g_strdup_printf (_("Open With “%s”"), app_name);
- action_tooltip = g_strdup_printf (_("Open this attachment in %s"), app_name);
- } else {
- action_label = g_strdup (_("Open With Default Application"));
- action_tooltip = g_strdup (_("Open this attachment in default application"));
- }
+ menu_item = g_menu_item_new (label, NULL);
+ g_menu_item_set_action_and_target_value (menu_item, "standard.EAttachmentView::open-with-app", g_variant_new_int32 (op_id));
+ g_menu_item_set_icon (menu_item, app_icon);
+ g_menu_append_item (priv->open_with_apps_menu, menu_item);
+ g_clear_object (&menu_item);
- action = gtk_action_new (
- action_name, action_label, action_tooltip, NULL);
+ if (app_info)
+ g_hash_table_insert (priv->open_with_apps_hash, GINT_TO_POINTER (op_id), g_object_ref (app_info));
- gtk_action_set_gicon (action, app_icon);
+ op_id++;
- if (app_info) {
- g_object_set_data_full (
- G_OBJECT (action),
- "app-info", g_object_ref (app_info),
- (GDestroyNotify) g_object_unref);
- }
-
- g_object_set_data_full (
- G_OBJECT (action),
- "attachment", g_object_ref (attachment),
- (GDestroyNotify) g_object_unref);
-
- g_signal_connect (
- action, "activate",
- G_CALLBACK (action_open_with_app_info_cb), view);
-
- gtk_action_group_add_action (action_group, action);
-
- gtk_ui_manager_add_ui (
- priv->ui_manager, priv->merge_id,
- "/context/open-actions", action_name,
- action_name, GTK_UI_MANAGER_AUTO, FALSE);
-
- g_free (action_name);
- g_free (action_label);
- g_free (action_tooltip);
+ g_free (label);
if (!app_info) {
list = g_list_remove (list, app_info);
@@ -1139,42 +1060,131 @@ e_attachment_view_default_init (EAttachmentViewInterface *iface)
void
e_attachment_view_init (EAttachmentView *view)
{
+ static const gchar *eui =
+ ""
+ ""
+ " "
+ " "
+ " "
+ " "
+ " "
+ ""
+ ""
+ ""
+ ""
+ ""
+ " "
+ " "
+ ""
+ " "
+ " "
+ ""
+ "";
+
+ static const EUIActionEntry standard_entries[] = {
+
+ { "cancel",
+ "process-stop",
+ N_("_Cancel"),
+ NULL,
+ NULL,
+ action_cancel_cb, NULL, NULL, NULL },
+
+ { "open-with",
+ NULL,
+ N_("Open With Other Application…"),
+ NULL,
+ NULL,
+ action_open_with_cb, NULL, NULL, NULL },
+
+ { "save-all",
+ "document-save-as",
+ N_("S_ave All"),
+ NULL,
+ NULL,
+ action_save_all_cb, NULL, NULL, NULL },
+
+ { "save-as",
+ "document-save-as",
+ N_("Sa_ve As"),
+ NULL,
+ NULL,
+ action_save_as_cb, NULL, NULL, NULL },
+
+ /* Alternate "save-all" label, for when
+ * the attachment store has one row. */
+ { "save-one",
+ "document-save-as",
+ N_("Save _As"),
+ NULL,
+ NULL,
+ action_save_all_cb, NULL, NULL, NULL },
+
+ { "EAttachmentView::open-with-app",
+ NULL,
+ N_("Open with…"),
+ NULL,
+ NULL,
+ action_open_with_app_cb, "i", NULL, NULL }
+ };
+
+ static const EUIActionEntry editable_entries[] = {
+
+ { "add",
+ "list-add",
+ N_("A_dd Attachment…"),
+ NULL,
+ N_("Attach a file"),
+ action_add_cb, NULL, NULL, NULL },
+
+ { "add-uri",
+ "emblem-web",
+ N_("Add _URI…"),
+ NULL,
+ N_("Attach a URI"),
+ action_add_uri_cb, NULL, NULL, NULL },
+
+ { "properties",
+ "document-properties",
+ N_("_Properties"),
+ NULL,
+ NULL,
+ action_properties_cb, NULL, NULL, NULL },
+
+ { "reload",
+ "view-refresh",
+ N_("Re_load"),
+ NULL,
+ N_("Reload attachment content"),
+ action_reload_cb, NULL, NULL, NULL },
+
+ { "remove",
+ "list-remove",
+ N_("_Remove"),
+ NULL,
+ NULL,
+ action_remove_cb, NULL, NULL, NULL }
+ };
+
EAttachmentViewPrivate *priv;
- GtkUIManager *ui_manager;
- GtkActionGroup *action_group;
- GError *error = NULL;
priv = e_attachment_view_get_private (view);
- ui_manager = gtk_ui_manager_new ();
- priv->merge_id = gtk_ui_manager_new_merge_id (ui_manager);
- priv->ui_manager = ui_manager;
+ priv->ui_manager = e_ui_manager_new ();
+ priv->open_with_apps_menu = g_menu_new ();
+ priv->open_with_apps_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
- action_group = e_attachment_view_add_action_group (view, "standard");
+ e_ui_manager_add_actions_with_eui_data (priv->ui_manager, "standard", NULL,
+ standard_entries, G_N_ELEMENTS (standard_entries), view, eui);
- gtk_action_group_add_actions (
- action_group, standard_entries,
- G_N_ELEMENTS (standard_entries), view);
-
- action_group = e_attachment_view_add_action_group (view, "editable");
+ e_ui_manager_add_actions (priv->ui_manager, "editable", NULL,
+ editable_entries, G_N_ELEMENTS (editable_entries), view);
e_binding_bind_property (
view, "editable",
- action_group, "visible",
+ e_ui_manager_get_action_group (priv->ui_manager, "editable"), "visible",
G_BINDING_BIDIRECTIONAL |
G_BINDING_SYNC_CREATE);
- gtk_action_group_add_actions (
- action_group, editable_entries,
- G_N_ELEMENTS (editable_entries), view);
-
- e_attachment_view_add_action_group (view, "openwith");
-
- /* Because we are loading from a hard-coded string, there is
- * no chance of I/O errors. Failure here implies a malformed
- * UI definition. Full stop. */
- gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
- if (error != NULL)
- g_error ("%s", error->message);
attachment_view_init_drag_dest (view);
@@ -1205,6 +1215,11 @@ e_attachment_view_init (EAttachmentView *view)
g_signal_connect (
view, "drag-data-received",
G_CALLBACK (attachment_view_text_x_moz_url), NULL);
+
+ g_signal_connect (priv->ui_manager, "create-item",
+ G_CALLBACK (e_attachment_view_ui_manager_create_item_cb), view);
+
+ e_ui_manager_set_action_groups_widget (priv->ui_manager, GTK_WIDGET (view));
}
void
@@ -1215,6 +1230,8 @@ e_attachment_view_dispose (EAttachmentView *view)
priv = e_attachment_view_get_private (view);
g_clear_pointer (&priv->target_list, gtk_target_list_unref);
+ g_clear_pointer (&priv->open_with_apps_hash, g_hash_table_unref);
+ g_clear_object (&priv->open_with_apps_menu);
g_clear_object (&priv->ui_manager);
}
@@ -2114,91 +2131,58 @@ e_attachment_view_drag_data_received (EAttachmentView *view,
gtk_drag_finish (drag_context, FALSE, FALSE, time);
}
-GtkAction *
+EUIAction *
e_attachment_view_get_action (EAttachmentView *view,
const gchar *action_name)
{
- GtkUIManager *ui_manager;
+ EUIManager *ui_manager;
g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
g_return_val_if_fail (action_name != NULL, NULL);
ui_manager = e_attachment_view_get_ui_manager (view);
- return e_lookup_action (ui_manager, action_name);
+ return e_ui_manager_get_action (ui_manager, action_name);
}
-GtkActionGroup *
-e_attachment_view_add_action_group (EAttachmentView *view,
- const gchar *group_name)
-{
- GtkActionGroup *action_group;
- GtkUIManager *ui_manager;
- const gchar *domain;
-
- g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
- g_return_val_if_fail (group_name != NULL, NULL);
-
- ui_manager = e_attachment_view_get_ui_manager (view);
- domain = GETTEXT_PACKAGE;
-
- action_group = gtk_action_group_new (group_name);
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
- g_object_unref (action_group);
-
- return action_group;
-}
-
-GtkActionGroup *
+EUIActionGroup *
e_attachment_view_get_action_group (EAttachmentView *view,
const gchar *group_name)
{
- GtkUIManager *ui_manager;
+ EUIManager *ui_manager;
g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
g_return_val_if_fail (group_name != NULL, NULL);
ui_manager = e_attachment_view_get_ui_manager (view);
- return e_lookup_action_group (ui_manager, group_name);
-}
-
-static void
-e_attachment_view_menu_deactivate_cb (GtkMenu *popup_menu,
- gpointer user_data)
-{
- g_return_if_fail (GTK_IS_MENU (popup_menu));
-
- g_signal_handlers_disconnect_by_func (popup_menu, e_attachment_view_menu_deactivate_cb, user_data);
- gtk_menu_detach (popup_menu);
+ return e_ui_manager_get_action_group (ui_manager, group_name);
}
GtkWidget *
e_attachment_view_get_popup_menu (EAttachmentView *view)
{
- GtkUIManager *ui_manager;
- GtkWidget *menu;
+ EUIManager *ui_manager;
+ GObject *ui_object;
+ GtkMenu *menu;
g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
ui_manager = e_attachment_view_get_ui_manager (view);
- menu = gtk_ui_manager_get_widget (ui_manager, "/context");
- g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
+ ui_object = e_ui_manager_create_item (ui_manager, "context");
+ g_return_val_if_fail (G_IS_MENU_MODEL (ui_object), NULL);
- if (!gtk_menu_get_attach_widget (GTK_MENU (menu))) {
- gtk_menu_attach_to_widget (GTK_MENU (menu),
- GTK_WIDGET (view),
- NULL);
- g_signal_connect (
- menu, "deactivate",
- G_CALLBACK (e_attachment_view_menu_deactivate_cb), NULL);
- }
+ menu = GTK_MENU (gtk_menu_new_from_model (G_MENU_MODEL (ui_object)));
- return menu;
+ g_clear_object (&ui_object);
+
+ gtk_menu_attach_to_widget (menu, GTK_WIDGET (view), NULL);
+ e_util_connect_menu_detach_after_deactivate (menu);
+
+ return GTK_WIDGET (menu);
}
-GtkUIManager *
+EUIManager *
e_attachment_view_get_ui_manager (EAttachmentView *view)
{
EAttachmentViewPrivate *priv;
diff --git a/src/e-util/e-attachment-view.h b/src/e-util/e-attachment-view.h
index a3b4ec56bc..3c7c90f5aa 100644
--- a/src/e-util/e-attachment-view.h
+++ b/src/e-util/e-attachment-view.h
@@ -27,6 +27,9 @@
#include
#include
+#include
+#include
+#include
/* Standard GObject macros */
#define E_TYPE_ATTACHMENT_VIEW \
@@ -104,8 +107,9 @@ struct _EAttachmentViewPrivate {
GdkDragAction drag_actions;
/* Popup Menu Management */
- GtkUIManager *ui_manager;
- guint merge_id;
+ EUIManager *ui_manager;
+ GMenu *open_with_apps_menu;
+ GHashTable *open_with_apps_hash; /* gint index ~> GAppInfo * */
/* Multi-DnD State */
GList *event_list;
@@ -228,17 +232,14 @@ void e_attachment_view_drag_data_received
guint time);
/* Popup Menu Management */
-GtkAction * e_attachment_view_get_action (EAttachmentView *view,
+EUIAction * e_attachment_view_get_action (EAttachmentView *view,
const gchar *action_name);
-GtkActionGroup *e_attachment_view_add_action_group
- (EAttachmentView *view,
- const gchar *group_name);
-GtkActionGroup *e_attachment_view_get_action_group
+EUIActionGroup *e_attachment_view_get_action_group
(EAttachmentView *view,
const gchar *group_name);
GtkWidget * e_attachment_view_get_popup_menu
(EAttachmentView *view);
-GtkUIManager * e_attachment_view_get_ui_manager
+EUIManager * e_attachment_view_get_ui_manager
(EAttachmentView *view);
void e_attachment_view_update_actions
(EAttachmentView *view);
diff --git a/src/e-util/e-charset-combo-box.c b/src/e-util/e-charset-combo-box.c
index 2f9001ee9d..11e59c4d02 100644
--- a/src/e-util/e-charset-combo-box.c
+++ b/src/e-util/e-charset-combo-box.c
@@ -20,24 +20,18 @@
#include "evolution-config.h"
-#include "e-charset-combo-box.h"
-
#include
#include "e-charset.h"
-#include "e-misc-utils.h"
-#define DEFAULT_CHARSET "UTF-8"
-#define OTHER_VALUE G_MAXINT
+#include "e-charset-combo-box.h"
-struct _ECharsetComboBoxPrivate {
- GtkActionGroup *action_group;
- GtkRadioAction *other_action;
- GHashTable *charset_index;
+struct _ECharsetComboBox {
+ GtkComboBox parent;
/* Used when the user clicks Cancel in the character set
* dialog. Reverts to the previous combo box setting. */
- gint previous_index;
+ gchar *previous_id;
/* When setting the character set programmatically, this
* prevents the custom character set dialog from running. */
@@ -49,7 +43,7 @@ enum {
PROP_CHARSET
};
-G_DEFINE_TYPE_WITH_PRIVATE (ECharsetComboBox, e_charset_combo_box, E_TYPE_ACTION_COMBO_BOX)
+G_DEFINE_TYPE (ECharsetComboBox, e_charset_combo_box, GTK_TYPE_COMBO_BOX)
static void
charset_combo_box_entry_changed_cb (GtkEntry *entry,
@@ -70,7 +64,6 @@ charset_combo_box_run_dialog (ECharsetComboBox *combo_box)
GtkEntry *entry;
GtkWidget *container;
GtkWidget *widget;
- GObject *object;
gpointer parent;
const gchar *charset;
@@ -83,8 +76,7 @@ charset_combo_box_run_dialog (ECharsetComboBox *combo_box)
parent = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
- object = G_OBJECT (combo_box->priv->other_action);
- charset = g_object_get_data (object, "charset");
+ charset = combo_box->previous_id;
widget = gtk_dialog_new_with_buttons (
_("Character Encoding"), parent,
@@ -135,43 +127,64 @@ charset_combo_box_run_dialog (ECharsetComboBox *combo_box)
* This will initialize the "OK" button to the proper state. */
gtk_entry_set_text (entry, charset);
- if (gtk_dialog_run (dialog) != GTK_RESPONSE_OK) {
- gint active;
+ if (gtk_dialog_run (dialog) == GTK_RESPONSE_OK) {
+ charset = gtk_entry_get_text (entry);
+ g_return_if_fail (charset != NULL && *charset != '\0');
+ /* for the cases where the user confirmed the choice without changing it,
+ it will force to set the correct id (from Other...) on the combo box */
+ g_clear_pointer (&combo_box->previous_id, g_free);
+
+ e_charset_combo_box_set_charset (combo_box, charset);
+ } else {
/* Revert to the previously selected character set. */
- combo_box->priv->block_dialog = TRUE;
- active = combo_box->priv->previous_index;
- gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), active);
- combo_box->priv->block_dialog = FALSE;
-
- goto exit;
+ combo_box->block_dialog = TRUE;
+ gtk_combo_box_set_active_id (GTK_COMBO_BOX (combo_box), combo_box->previous_id);
+ combo_box->block_dialog = FALSE;
}
- charset = gtk_entry_get_text (entry);
- g_return_if_fail (charset != NULL && *charset != '\0');
-
- g_object_set_data_full (
- object, "charset", g_strdup (charset),
- (GDestroyNotify) g_free);
-
-exit:
gtk_widget_destroy (GTK_WIDGET (dialog));
}
static void
-charset_combo_box_notify_charset_cb (ECharsetComboBox *combo_box)
+charset_combo_box_changed (GtkComboBox *combo_box)
{
- GtkToggleAction *action;
+ ECharsetComboBox *self = E_CHARSET_COMBO_BOX (combo_box);
+ const gchar *charset;
- action = GTK_TOGGLE_ACTION (combo_box->priv->other_action);
- if (!gtk_toggle_action_get_active (action))
+ /* Chain up to parent's changed() method. */
+ if (GTK_COMBO_BOX_CLASS (e_charset_combo_box_parent_class)->changed)
+ GTK_COMBO_BOX_CLASS (e_charset_combo_box_parent_class)->changed (combo_box);
+
+ if (self->block_dialog)
return;
- if (combo_box->priv->block_dialog)
- return;
+ charset = e_charset_combo_box_get_charset (self);
- /* "Other" action was selected by user. */
- charset_combo_box_run_dialog (combo_box);
+ /* the "Other..." value is an empty string */
+ if (charset && !*charset) {
+ charset_combo_box_run_dialog (self);
+ } else {
+ g_clear_pointer (&self->previous_id, g_free);
+ self->previous_id = g_strdup (charset);
+
+ g_object_notify (G_OBJECT (self), "charset");
+ }
+}
+
+static gboolean
+charset_combo_box_is_row_separator (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ gchar *charset = NULL;
+ gboolean separator;
+
+ gtk_tree_model_get (model, iter, E_CHARSET_COLUMN_VALUE, &charset, -1);
+ separator = (charset == NULL);
+ g_free (charset);
+
+ return separator;
}
static void
@@ -213,63 +226,12 @@ charset_combo_box_dispose (GObject *object)
{
ECharsetComboBox *self = E_CHARSET_COMBO_BOX (object);
- g_clear_object (&self->priv->action_group);
- g_clear_object (&self->priv->other_action);
-
- g_hash_table_remove_all (self->priv->charset_index);
+ g_clear_pointer (&self->previous_id, g_free);
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (e_charset_combo_box_parent_class)->dispose (object);
}
-static void
-charset_combo_box_finalize (GObject *object)
-{
- ECharsetComboBox *self = E_CHARSET_COMBO_BOX (object);
-
- g_hash_table_destroy (self->priv->charset_index);
-
- /* Chain up to parent's finalize() method. */
- G_OBJECT_CLASS (e_charset_combo_box_parent_class)->finalize (object);
-}
-
-static void
-charset_combo_box_constructed (GObject *object)
-{
- ECharsetComboBox *self = E_CHARSET_COMBO_BOX (object);
- GtkRadioAction *radio_action;
- GSList *group;
-
- /* Chain up to parent's constructed() method. */
- G_OBJECT_CLASS (e_charset_combo_box_parent_class)->constructed (object);
-
- radio_action = self->priv->other_action;
- group = gtk_radio_action_get_group (radio_action);
-
- e_action_combo_box_set_action (
- E_ACTION_COMBO_BOX (object), radio_action);
-
- e_action_combo_box_add_separator_after (
- E_ACTION_COMBO_BOX (object), g_slist_length (group));
-
- e_signal_connect_notify (
- object, "notify::charset",
- G_CALLBACK (charset_combo_box_notify_charset_cb), NULL);
-}
-
-static void
-charset_combo_box_changed (GtkComboBox *combo_box)
-{
- ECharsetComboBox *self = E_CHARSET_COMBO_BOX (combo_box);
-
- /* Chain up to parent's changed() method. */
- GTK_COMBO_BOX_CLASS (e_charset_combo_box_parent_class)->changed (combo_box);
-
- /* Notify -before- updating previous index. */
- g_object_notify (G_OBJECT (combo_box), "charset");
- self->priv->previous_index = gtk_combo_box_get_active (combo_box);
-}
-
static void
e_charset_combo_box_class_init (ECharsetComboBoxClass *class)
{
@@ -280,8 +242,6 @@ e_charset_combo_box_class_init (ECharsetComboBoxClass *class)
object_class->set_property = charset_combo_box_set_property;
object_class->get_property = charset_combo_box_get_property;
object_class->dispose = charset_combo_box_dispose;
- object_class->finalize = charset_combo_box_finalize;
- object_class->constructed = charset_combo_box_constructed;
combo_box_class = GTK_COMBO_BOX_CLASS (class);
combo_box_class->changed = charset_combo_box_changed;
@@ -293,56 +253,40 @@ e_charset_combo_box_class_init (ECharsetComboBoxClass *class)
"charset",
"Charset",
"The selected character set",
- "UTF-8",
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT));
+ "",
+ G_PARAM_READWRITE));
}
static void
-e_charset_combo_box_init (ECharsetComboBox *combo_box)
+e_charset_combo_box_init (ECharsetComboBox *self)
{
- GtkActionGroup *action_group;
- GtkRadioAction *radio_action;
- GHashTable *charset_index;
- GSList *group, *iter;
+ GtkComboBox *combo_box = GTK_COMBO_BOX (self);
+ GtkCellRenderer *renderer;
+ GtkListStore *list_store;
+ GtkTreeIter iter;
- action_group = gtk_action_group_new ("charset-combo-box-internal");
+ list_store = e_charset_create_list_store ();
- charset_index = g_hash_table_new_full (
- g_str_hash, g_str_equal,
- (GDestroyNotify) g_free,
- (GDestroyNotify) g_object_unref);
+ /* separator, both label and value are NULL */
+ gtk_list_store_append (list_store, &iter);
- combo_box->priv = e_charset_combo_box_get_instance_private (combo_box);
- combo_box->priv->action_group = action_group;
- combo_box->priv->charset_index = charset_index;
+ /* other option */
+ gtk_list_store_append (list_store, &iter);
+ gtk_list_store_set (list_store, &iter,
+ E_CHARSET_COLUMN_LABEL, _("Other…"),
+ E_CHARSET_COLUMN_VALUE, "",
+ -1);
- group = e_charset_add_radio_actions (
- action_group, "charset-", NULL, NULL, NULL);
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (self), renderer, TRUE);
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (self), renderer, "text", E_CHARSET_COLUMN_LABEL);
- /* Populate the character set index. */
- for (iter = group; iter != NULL; iter = iter->next) {
- GObject *object = iter->data;
- const gchar *charset;
+ gtk_combo_box_set_row_separator_func (combo_box, charset_combo_box_is_row_separator, NULL, NULL);
- charset = g_object_get_data (object, "charset");
- g_return_if_fail (charset != NULL);
+ gtk_combo_box_set_model (combo_box, GTK_TREE_MODEL (list_store));
+ gtk_combo_box_set_id_column (combo_box, E_CHARSET_COLUMN_VALUE);
- g_hash_table_insert (
- charset_index, g_strdup (charset),
- g_object_ref (object));
- }
-
- /* Note the "other" action is not included in the index. */
-
- radio_action = gtk_radio_action_new (
- "charset-other", _("Other…"), NULL, NULL, OTHER_VALUE);
-
- g_object_set_data (G_OBJECT (radio_action), "charset", (gpointer) "");
-
- gtk_radio_action_set_group (radio_action, group);
-
- combo_box->priv->other_action = radio_action;
+ g_object_unref (list_store);
}
GtkWidget *
@@ -354,40 +298,53 @@ e_charset_combo_box_new (void)
const gchar *
e_charset_combo_box_get_charset (ECharsetComboBox *combo_box)
{
- GtkRadioAction *radio_action;
-
g_return_val_if_fail (E_IS_CHARSET_COMBO_BOX (combo_box), NULL);
- radio_action = combo_box->priv->other_action;
- radio_action = e_radio_action_get_current_action (radio_action);
-
- return g_object_get_data (G_OBJECT (radio_action), "charset");
+ return gtk_combo_box_get_active_id (GTK_COMBO_BOX (combo_box));
}
void
e_charset_combo_box_set_charset (ECharsetComboBox *combo_box,
const gchar *charset)
{
- GHashTable *charset_index;
- GtkRadioAction *radio_action;
-
g_return_if_fail (E_IS_CHARSET_COMBO_BOX (combo_box));
if (charset == NULL || *charset == '\0')
charset = "UTF-8";
- charset_index = combo_box->priv->charset_index;
- radio_action = g_hash_table_lookup (charset_index, charset);
+ if (g_strcmp0 (charset, combo_box->previous_id) == 0)
+ return;
- if (radio_action == NULL) {
- radio_action = combo_box->priv->other_action;
- g_object_set_data_full (
- G_OBJECT (radio_action),
- "charset", g_strdup (charset),
- (GDestroyNotify) g_free);
+ combo_box->block_dialog = TRUE;
+
+ g_clear_pointer (&combo_box->previous_id, g_free);
+ combo_box->previous_id = g_strdup (charset);
+
+ if (!gtk_combo_box_set_active_id (GTK_COMBO_BOX (combo_box), charset)) {
+ GtkListStore *list_store;
+ GtkTreeIter iter;
+ gchar *escaped_name;
+ gchar **str_array;
+
+ /* Escape underlines in the character set name so
+ * they're not treated as GtkLabel mnemonics. */
+ str_array = g_strsplit (charset, "_", -1);
+ escaped_name = g_strjoinv ("__", str_array);
+ g_strfreev (str_array);
+
+ list_store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)));
+ gtk_list_store_prepend (list_store, &iter);
+ gtk_list_store_set (list_store, &iter,
+ E_CHARSET_COLUMN_LABEL, escaped_name,
+ E_CHARSET_COLUMN_VALUE, charset,
+ -1);
+
+ g_free (escaped_name);
+
+ gtk_combo_box_set_active_id (GTK_COMBO_BOX (combo_box), charset);
}
- combo_box->priv->block_dialog = TRUE;
- gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (radio_action), TRUE);
- combo_box->priv->block_dialog = FALSE;
+ g_object_notify (G_OBJECT (combo_box), "charset");
+
+ combo_box->block_dialog = FALSE;
}
diff --git a/src/e-util/e-charset-combo-box.h b/src/e-util/e-charset-combo-box.h
index 52076d34c4..65ddb73e66 100644
--- a/src/e-util/e-charset-combo-box.h
+++ b/src/e-util/e-charset-combo-box.h
@@ -25,43 +25,13 @@
#ifndef E_CHARSET_COMBO_BOX_H
#define E_CHARSET_COMBO_BOX_H
-#include
-
-/* Standard GObject macros */
-#define E_TYPE_CHARSET_COMBO_BOX \
- (e_charset_combo_box_get_type ())
-#define E_CHARSET_COMBO_BOX(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST \
- ((obj), E_TYPE_CHARSET_COMBO_BOX, ECharsetComboBox))
-#define E_CHARSET_COMBO_BOX_CLASS(cls) \
- (G_TYPE_CHECK_CLASS_CAST \
- ((cls), E_TYPE_CHARSET_COMBO_BOX, ECharsetComboBoxClass))
-#define E_IS_CHARSET_COMBO_BOX(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE \
- ((obj), E_TYPE_CHARSET_COMBO_BOX))
-#define E_IS_CHARSET_COMBO_BOX_CLASS(cls) \
- (G_TYPE_CHECK_CLASS_TYPE \
- ((cls), E_TYPE_CHARSET_COMBO_BOX))
-#define E_CHARSET_COMBO_BOX_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS \
- ((obj), E_TYPE_CHARSET_COMBO_BOX, ECharsetComboBoxClass))
+#include
G_BEGIN_DECLS
-typedef struct _ECharsetComboBox ECharsetComboBox;
-typedef struct _ECharsetComboBoxClass ECharsetComboBoxClass;
-typedef struct _ECharsetComboBoxPrivate ECharsetComboBoxPrivate;
+#define E_TYPE_CHARSET_COMBO_BOX (e_charset_combo_box_get_type ())
+G_DECLARE_FINAL_TYPE (ECharsetComboBox, e_charset_combo_box, E, CHARSET_COMBO_BOX, GtkComboBox)
-struct _ECharsetComboBox {
- EActionComboBox parent;
- ECharsetComboBoxPrivate *priv;
-};
-
-struct _ECharsetComboBoxClass {
- EActionComboBoxClass parent_class;
-};
-
-GType e_charset_combo_box_get_type (void) G_GNUC_CONST;
GtkWidget * e_charset_combo_box_new (void);
const gchar * e_charset_combo_box_get_charset (ECharsetComboBox *combo_box);
void e_charset_combo_box_set_charset (ECharsetComboBox *combo_box,
diff --git a/src/e-util/e-charset.c b/src/e-util/e-charset.c
index 9e51f69735..1e4d448837 100644
--- a/src/e-util/e-charset.c
+++ b/src/e-util/e-charset.c
@@ -112,155 +112,36 @@ static ECharset charsets[] = {
{ "ISO-8859-15", E_CHARSET_WESTERN_EUROPEAN_NEW, NULL },
};
-/**
- * e_charset_add_radio_actions:
- * @action_group: a #GtkActionGroup
- * @action_prefix: a prefix for action names, or %NULL
- * @default_charset: the default character set, or %NULL to use the
- * locale character set
- * @callback: a callback function for actions in the group, or %NULL
- * @user_data: user data to be passed to @callback, or %NULL
- *
- * Adds a set of #GtkRadioActions for available character sets to
- * @action_group. The @default_charset (or locale character set if
- * @default_charset is %NULL) will be added first, and selected by
- * default (except that ISO-8859-1 will always be used instead of
- * US-ASCII). Any other character sets of the same language class as
- * the default will be added next, followed by the remaining character
- * sets.
- *
- * Returns: the radio action group
- **/
-GSList *
-e_charset_add_radio_actions (GtkActionGroup *action_group,
- const gchar *action_prefix,
- const gchar *default_charset,
- GCallback callback,
- gpointer user_data)
+static gchar *
+e_charset_labelize (const ECharset *charset)
{
- GtkRadioAction *action = NULL;
- GSList *group = NULL;
- const gchar *locale_charset;
- gint def, ii;
+ gchar *escaped_name;
+ gchar *charset_label;
+ gchar **str_array;
- g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), NULL);
+ /* Escape underlines in the character set name so
+ * they're not treated as GtkLabel mnemonics. */
+ str_array = g_strsplit (charset->name, "_", -1);
+ escaped_name = g_strjoinv ("__", str_array);
+ g_strfreev (str_array);
- if (action_prefix == NULL)
- action_prefix = "";
+ if (charset->subclass != NULL)
+ charset_label = g_strdup_printf (
+ "%s, %s (%s)",
+ gettext (classnames[charset->class]),
+ gettext (charset->subclass),
+ escaped_name);
+ else if (charsets->class != E_CHARSET_UNKNOWN)
+ charset_label = g_strdup_printf (
+ "%s (%s)",
+ gettext (classnames[charset->class]),
+ escaped_name);
+ else
+ charset_label = g_steal_pointer (&escaped_name);
- g_get_charset (&locale_charset);
- if (!g_ascii_strcasecmp (locale_charset, "US-ASCII"))
- locale_charset = "ISO-8859-1";
+ g_free (escaped_name);
- if (default_charset == NULL)
- default_charset = locale_charset;
- for (def = 0; def < G_N_ELEMENTS (charsets); def++)
- if (!g_ascii_strcasecmp (charsets[def].name, default_charset))
- break;
-
- for (ii = 0; ii < G_N_ELEMENTS (charsets); ii++) {
- const gchar *charset_name;
- gchar *action_name;
- gchar *escaped_name;
- gchar *charset_label;
- gchar **str_array;
-
- charset_name = charsets[ii].name;
- action_name = g_strconcat (action_prefix, charset_name, NULL);
-
- /* Escape underlines in the character set name so
- * they're not treated as GtkLabel mnemonics. */
- str_array = g_strsplit (charset_name, "_", -1);
- escaped_name = g_strjoinv ("__", str_array);
- g_strfreev (str_array);
-
- if (charsets[ii].subclass != NULL)
- charset_label = g_strdup_printf (
- "%s, %s (%s)",
- gettext (classnames[charsets[ii].class]),
- gettext (charsets[ii].subclass),
- escaped_name);
- else if (charsets[ii].class != E_CHARSET_UNKNOWN)
- charset_label = g_strdup_printf (
- "%s (%s)",
- gettext (classnames[charsets[ii].class]),
- escaped_name);
- else
- charset_label = g_strdup (escaped_name);
-
- /* XXX Add a tooltip! */
- action = gtk_radio_action_new (
- action_name, charset_label, NULL, NULL, ii);
-
- /* Character set name is static so no need to free it. */
- g_object_set_data (
- G_OBJECT (action), "charset",
- (gpointer) charset_name);
-
- gtk_radio_action_set_group (action, group);
- group = gtk_radio_action_get_group (action);
-
- if (callback != NULL)
- g_signal_connect (
- action, "changed", callback, user_data);
-
- gtk_action_group_add_action (
- action_group, GTK_ACTION (action));
-
- g_object_unref (action);
-
- g_free (action_name);
- g_free (escaped_name);
- g_free (charset_label);
- }
-
- if (def == G_N_ELEMENTS (charsets)) {
- const gchar *charset_name;
- gchar *action_name;
- gchar *charset_label;
- gchar **str_array;
-
- charset_name = default_charset;
- action_name = g_strconcat (action_prefix, charset_name, NULL);
-
- /* Escape underlines in the character set name so
- * they're not treated as GtkLabel mnemonics. */
- str_array = g_strsplit (charset_name, "_", -1);
- charset_label = g_strjoinv ("__", str_array);
- g_strfreev (str_array);
-
- /* XXX Add a tooltip! */
- action = gtk_radio_action_new (
- action_name, charset_label, NULL, NULL, def);
-
- /* Character set name may NOT be static,
- * so we do need to duplicate the string. */
- g_object_set_data_full (
- G_OBJECT (action), "charset",
- g_strdup (charset_name),
- (GDestroyNotify) g_free);
-
- gtk_radio_action_set_group (action, group);
- group = gtk_radio_action_get_group (action);
-
- if (callback != NULL)
- g_signal_connect (
- action, "changed", callback, user_data);
-
- gtk_action_group_add_action (
- action_group, GTK_ACTION (action));
-
- g_object_unref (action);
-
- g_free (action_name);
- g_free (charset_label);
- }
-
- /* Any of the actions in the action group will do. */
- if (action != NULL)
- gtk_radio_action_set_current_value (action, def);
-
- return group;
+ return charset_label;
}
/**
@@ -274,7 +155,7 @@ e_charset_add_radio_actions (GtkActionGroup *action_group,
*
* This does not add a "Default" option.
*
- * Since: 3.54
+ * Since: 3.56
**/
void
e_charset_add_to_g_menu (GMenu *menu,
@@ -291,31 +172,10 @@ e_charset_add_to_g_menu (GMenu *menu,
for (ii = 0; ii < G_N_ELEMENTS (charsets); ii++) {
GMenuItem *menu_item;
const gchar *charset_name;
- gchar *escaped_name;
gchar *charset_label;
- gchar **str_array;
charset_name = charsets[ii].name;
-
- /* Escape underlines in the character set name so
- * they're not treated as GtkLabel mnemonics. */
- str_array = g_strsplit (charset_name, "_", -1);
- escaped_name = g_strjoinv ("__", str_array);
- g_strfreev (str_array);
-
- if (charsets[ii].subclass != NULL)
- charset_label = g_strdup_printf (
- "%s, %s (%s)",
- gettext (classnames[charsets[ii].class]),
- gettext (charsets[ii].subclass),
- escaped_name);
- else if (charsets[ii].class != E_CHARSET_UNKNOWN)
- charset_label = g_strdup_printf (
- "%s (%s)",
- gettext (classnames[charsets[ii].class]),
- escaped_name);
- else
- charset_label = g_strdup (escaped_name);
+ charset_label = e_charset_labelize (&(charsets[ii]));
menu_item = g_menu_item_new (charset_label, NULL);
g_menu_item_set_action_and_target (menu_item, action_name, "s", charset_name);
@@ -323,7 +183,6 @@ e_charset_add_to_g_menu (GMenu *menu,
g_object_unref (menu_item);
- g_free (escaped_name);
g_free (charset_label);
}
@@ -331,3 +190,42 @@ e_charset_add_to_g_menu (GMenu *menu,
g_clear_object (§ion);
}
+
+/**
+ * e_charset_create_list_store:
+ *
+ * Creates a new #GtkListStore containing two columns, E_CHARSET_COLUMN_LABEL with the label
+ * of the charset, to be shown tin the GUI, and E_CHARSET_COLUMN_VALUE with the actual
+ * charset value.
+ *
+ * Returns: (transfer full): newly created #GtkListStore
+ *
+ * Since: 3.56
+ **/
+GtkListStore *
+e_charset_create_list_store (void)
+{
+ GtkListStore *list_store;
+ guint ii;
+
+ list_store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+
+ for (ii = 0; ii < G_N_ELEMENTS (charsets); ii++) {
+ GtkTreeIter iter;
+ const gchar *charset_name;
+ gchar *charset_label;
+
+ charset_name = charsets[ii].name;
+ charset_label = e_charset_labelize (&(charsets[ii]));
+
+ gtk_list_store_append (list_store, &iter);
+ gtk_list_store_set (list_store, &iter,
+ E_CHARSET_COLUMN_LABEL, charset_label,
+ E_CHARSET_COLUMN_VALUE, charset_name,
+ -1);
+
+ g_free (charset_label);
+ }
+
+ return list_store;
+}
diff --git a/src/e-util/e-charset.h b/src/e-util/e-charset.h
index 1d2ca50368..a3c702e6eb 100644
--- a/src/e-util/e-charset.h
+++ b/src/e-util/e-charset.h
@@ -28,15 +28,14 @@
G_BEGIN_DECLS
-GSList * e_charset_add_radio_actions (GtkActionGroup *action_group,
- const gchar *action_prefix,
- const gchar *default_charset,
- GCallback callback,
- gpointer user_data);
-
void e_charset_add_to_g_menu (GMenu *menu,
const gchar *action_name);
+#define E_CHARSET_COLUMN_LABEL 0
+#define E_CHARSET_COLUMN_VALUE 1
+
+GtkListStore * e_charset_create_list_store (void);
+
G_END_DECLS
#endif /* E_CHARSET_H */
diff --git a/src/e-util/e-content-editor.c b/src/e-util/e-content-editor.c
index 18119143a0..1acebe5040 100644
--- a/src/e-util/e-content-editor.c
+++ b/src/e-util/e-content-editor.c
@@ -1972,7 +1972,7 @@ e_content_editor_insert_image (EContentEditor *editor,
void
e_content_editor_insert_emoticon (EContentEditor *editor,
- EEmoticon *emoticon)
+ const EEmoticon *emoticon)
{
EContentEditorInterface *iface;
diff --git a/src/e-util/e-content-editor.h b/src/e-util/e-content-editor.h
index 6a34193406..b729846785 100644
--- a/src/e-util/e-content-editor.h
+++ b/src/e-util/e-content-editor.h
@@ -83,7 +83,7 @@ struct _EContentEditorInterface {
const gchar *uri);
void (*insert_emoticon) (EContentEditor *editor,
- EEmoticon *emoticon);
+ const EEmoticon *emoticon);
void (*move_caret_on_coordinates) (EContentEditor *editor,
gint x,
@@ -586,7 +586,7 @@ void e_content_editor_insert_image (EContentEditor *editor,
const gchar *uri);
void e_content_editor_insert_emoticon
(EContentEditor *editor,
- EEmoticon *emoticon);
+ const EEmoticon *emoticon);
void e_content_editor_move_caret_on_coordinates
(EContentEditor *editor,
diff --git a/src/e-util/e-emoticon-action.c b/src/e-util/e-emoticon-action.c
deleted file mode 100644
index 51ff5fb538..0000000000
--- a/src/e-util/e-emoticon-action.c
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * e-emoticon-action.c
- *
- * Copyright (C) 2008 Novell, Inc.
- * Copyright (C) 2012 Dan Vrátil
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU Lesser General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include "e-emoticon-action.h"
-
-#include "e-emoticon-chooser.h"
-#include "e-emoticon-chooser-menu.h"
-#include "e-emoticon-tool-button.h"
-
-struct _EEmoticonActionPrivate {
- GList *choosers;
- EEmoticonChooser *current_chooser;
-};
-
-enum {
- PROP_0,
- PROP_CURRENT_FACE
-};
-
-/* Forward Declarations */
-static void e_emoticon_action_interface_init
- (EEmoticonChooserInterface *interface);
-
-G_DEFINE_TYPE_WITH_CODE (EEmoticonAction, e_emoticon_action, GTK_TYPE_ACTION,
- G_ADD_PRIVATE (EEmoticonAction)
- G_IMPLEMENT_INTERFACE (E_TYPE_EMOTICON_CHOOSER, e_emoticon_action_interface_init))
-
-static void
-emoticon_action_proxy_item_activated_cb (EEmoticonAction *action,
- EEmoticonChooser *chooser)
-{
- action->priv->current_chooser = chooser;
-
- g_signal_emit_by_name (action, "item-activated");
-}
-
-static void
-emoticon_action_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- switch (property_id) {
- case PROP_CURRENT_FACE:
- e_emoticon_chooser_set_current_emoticon (
- E_EMOTICON_CHOOSER (object),
- g_value_get_boxed (value));
- return;
- }
-
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-}
-
-static void
-emoticon_action_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec)
-{
- switch (property_id) {
- case PROP_CURRENT_FACE:
- g_value_set_boxed (
- value, e_emoticon_chooser_get_current_emoticon (
- E_EMOTICON_CHOOSER (object)));
- return;
- }
-
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-}
-
-static void
-emoticon_action_finalize (GObject *object)
-{
- EEmoticonAction *self = E_EMOTICON_ACTION (object);
-
- g_list_free (self->priv->choosers);
-
- /* Chain up to parent's finalize() method. */
- G_OBJECT_CLASS (e_emoticon_action_parent_class)->finalize (object);
-}
-
-static void
-emoticon_action_activate (GtkAction *action)
-{
- EEmoticonAction *self = E_EMOTICON_ACTION (action);
-
- self->priv->current_chooser = NULL;
-}
-
-static GtkWidget *
-emoticon_action_create_menu_item (GtkAction *action)
-{
- GtkWidget *item;
- GtkWidget *menu;
-
- item = gtk_image_menu_item_new ();
- menu = gtk_action_create_menu (action);
- gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
- gtk_widget_show (menu);
-
- return item;
-}
-
-static GtkWidget *
-emoticon_action_create_tool_item (GtkAction *action)
-{
- return GTK_WIDGET (e_emoticon_tool_button_new ());
-}
-
-static void
-emoticon_action_connect_proxy (GtkAction *action,
- GtkWidget *proxy)
-{
- EEmoticonAction *self = E_EMOTICON_ACTION (action);
-
- if (!E_IS_EMOTICON_CHOOSER (proxy))
- goto chainup;
-
- if (g_list_find (self->priv->choosers, proxy) != NULL)
- goto chainup;
-
- g_signal_connect_swapped (
- proxy, "item-activated",
- G_CALLBACK (emoticon_action_proxy_item_activated_cb), action);
-
-chainup:
- /* Chain up to parent's connect_proxy() method. */
- GTK_ACTION_CLASS (e_emoticon_action_parent_class)->connect_proxy (action, proxy);
-}
-
-static void
-emoticon_action_disconnect_proxy (GtkAction *action,
- GtkWidget *proxy)
-{
- EEmoticonAction *self = E_EMOTICON_ACTION (action);
-
- self->priv->choosers = g_list_remove (self->priv->choosers, proxy);
-
- /* Chain up to parent's disconnect_proxy() method. */
- GTK_ACTION_CLASS (e_emoticon_action_parent_class)->disconnect_proxy (action, proxy);
-}
-
-static GtkWidget *
-emoticon_action_create_menu (GtkAction *action)
-{
- EEmoticonAction *self = E_EMOTICON_ACTION (action);
- GtkWidget *widget;
-
- widget = e_emoticon_chooser_menu_new ();
-
- g_signal_connect_swapped (
- widget, "item-activated",
- G_CALLBACK (emoticon_action_proxy_item_activated_cb), action);
-
- self->priv->choosers = g_list_prepend (self->priv->choosers, widget);
-
- return widget;
-}
-
-static EEmoticon *
-emoticon_action_get_current_emoticon (EEmoticonChooser *chooser)
-{
- EEmoticonAction *self = E_EMOTICON_ACTION (chooser);
- EEmoticon *emoticon = NULL;
-
- if (self->priv->current_chooser)
- emoticon = e_emoticon_chooser_get_current_emoticon (self->priv->current_chooser);
-
- return emoticon;
-}
-
-static void
-emoticon_action_set_current_emoticon (EEmoticonChooser *chooser,
- EEmoticon *emoticon)
-{
- EEmoticonAction *self = E_EMOTICON_ACTION (chooser);
- GList *iter;
-
- for (iter = self->priv->choosers; iter != NULL; iter = iter->next) {
- EEmoticonChooser *proxy_chooser = iter->data;
-
- e_emoticon_chooser_set_current_emoticon (proxy_chooser, emoticon);
- }
-}
-
-static void
-e_emoticon_action_class_init (EEmoticonActionClass *class)
-{
- GObjectClass *object_class;
- GtkActionClass *action_class;
-
- object_class = G_OBJECT_CLASS (class);
- object_class->set_property = emoticon_action_set_property;
- object_class->get_property = emoticon_action_get_property;
- object_class->finalize = emoticon_action_finalize;
-
- action_class = GTK_ACTION_CLASS (class);
- action_class->activate = emoticon_action_activate;
- action_class->create_menu_item = emoticon_action_create_menu_item;
- action_class->create_tool_item = emoticon_action_create_tool_item;
- action_class->connect_proxy = emoticon_action_connect_proxy;
- action_class->disconnect_proxy = emoticon_action_disconnect_proxy;
- action_class->create_menu = emoticon_action_create_menu;
-
- g_object_class_override_property (
- object_class, PROP_CURRENT_FACE, "current-emoticon");
-}
-
-static void
-e_emoticon_action_interface_init (EEmoticonChooserInterface *interface)
-{
- interface->get_current_emoticon = emoticon_action_get_current_emoticon;
- interface->set_current_emoticon = emoticon_action_set_current_emoticon;
-}
-
-static void
-e_emoticon_action_init (EEmoticonAction *action)
-{
- action->priv = e_emoticon_action_get_instance_private (action);
-}
-
-GtkAction *
-e_emoticon_action_new (const gchar *name,
- const gchar *label,
- const gchar *tooltip,
- const gchar *stock_id)
-{
- g_return_val_if_fail (name != NULL, NULL);
-
- return g_object_new (
- E_TYPE_EMOTICON_ACTION, "name", name, "label", label,
- "tooltip", tooltip, "stock-id", stock_id, NULL);
-}
diff --git a/src/e-util/e-emoticon-action.h b/src/e-util/e-emoticon-action.h
deleted file mode 100644
index 0e450e8750..0000000000
--- a/src/e-util/e-emoticon-action.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * e-emoticon-action.h
- *
- * Copyright (C) 2008 Novell, Inc.
- * Copyright (C) 2012 Dan Vrátil
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU Lesser General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
-#error "Only should be included directly."
-#endif
-
-#ifndef E_EMOTICON_ACTION_H
-#define E_EMOTICON_ACTION_H
-
-#include
-
-/* Standard GObject macros */
-#define E_TYPE_EMOTICON_ACTION \
- (e_emoticon_action_get_type ())
-#define E_EMOTICON_ACTION(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST \
- ((obj), E_TYPE_EMOTICON_ACTION, EEmoticonAction))
-#define E_EMOTICON_ACTION_CLASS(cls) \
- (G_TYPE_CHECK_CLASS_CAST \
- ((cls), E_TYPE_EMOTICON_ACTION, EEmoticonActionClass))
-#define E_IS_EMOTICON_ACTION(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE \
- ((obj), E_TYPE_EMOTICON_ACTION))
-#define E_IS_EMOTICON_ACTION_CLASS(cls) \
- (G_TYPE_CHECK_CLASS_TYPE \
- ((cls), E_TYPE_EMOTICON_ACTION))
-#define E_EMOTICON_ACTION_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS \
- ((obj), E_TYPE_EMOTICON_ACTION, EEmoticonActionClass))
-
-G_BEGIN_DECLS
-
-typedef struct _EEmoticonAction EEmoticonAction;
-typedef struct _EEmoticonActionClass EEmoticonActionClass;
-typedef struct _EEmoticonActionPrivate EEmoticonActionPrivate;
-
-struct _EEmoticonAction {
- GtkAction parent;
- EEmoticonActionPrivate *priv;
-};
-
-struct _EEmoticonActionClass {
- GtkActionClass parent_class;
-};
-
-GType e_emoticon_action_get_type (void) G_GNUC_CONST;
-GtkAction * e_emoticon_action_new (const gchar *name,
- const gchar *label,
- const gchar *tooltip,
- const gchar *stock_id);
-
-G_END_DECLS
-
-#endif /* E_EMOTICON_ACTION_H */
diff --git a/src/e-util/e-emoticon-tool-button.c b/src/e-util/e-emoticon-tool-button.c
deleted file mode 100644
index 3221320377..0000000000
--- a/src/e-util/e-emoticon-tool-button.c
+++ /dev/null
@@ -1,561 +0,0 @@
-/*
- * e-emoticon-tool-button.c
- *
- * Copyright (C) 2008 Novell, Inc.
- * Copyright (C) 2012 Dan Vrátil
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU Lesser General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include "evolution-config.h"
-
-#include "e-emoticon-tool-button.h"
-
-/* XXX The "button" aspects of this widget are based heavily on the
- * GtkComboBox tree-view implementation. Consider splitting it
- * into a reusable "button-with-an-empty-window" widget. */
-
-#include
-#include
-
-#include "e-emoticon-chooser.h"
-
-/* XXX Should calculate this dynamically. */
-#define NUM_ROWS 7
-#define NUM_COLS 3
-
-enum {
- PROP_0,
- PROP_CURRENT_EMOTICON,
- PROP_POPUP_SHOWN
-};
-
-enum {
- POPUP,
- POPDOWN,
- LAST_SIGNAL
-};
-
-struct _EEmoticonToolButtonPrivate {
- GtkWidget *active_button; /* not referenced */
- GtkWidget *table;
- GtkWidget *popover;
-
- guint popup_shown : 1;
- guint popup_in_progress : 1;
-};
-
-static guint signals[LAST_SIGNAL];
-
-/* Forward Declarations */
-static void e_emoticon_tool_button_interface_init
- (EEmoticonChooserInterface *interface);
-
-G_DEFINE_TYPE_WITH_CODE (EEmoticonToolButton, e_emoticon_tool_button, GTK_TYPE_TOGGLE_TOOL_BUTTON,
- G_ADD_PRIVATE (EEmoticonToolButton)
- G_IMPLEMENT_INTERFACE (E_TYPE_EMOTICON_CHOOSER, e_emoticon_tool_button_interface_init))
-
-/* XXX Copied from _gtk_toolbar_elide_underscores() */
-static gchar *
-emoticon_tool_button_elide_underscores (const gchar *original)
-{
- gchar *q, *result;
- const gchar *p, *end;
- gsize len;
- gboolean last_underscore;
-
- if (!original)
- return NULL;
-
- len = strlen (original);
- q = result = g_malloc (len + 1);
- last_underscore = FALSE;
-
- end = original + len;
- for (p = original; p < end; p++) {
- if (!last_underscore && *p == '_')
- last_underscore = TRUE;
- else {
- last_underscore = FALSE;
- if (original + 2 <= p && p + 1 <= end &&
- p[-2] == '(' && p[-1] == '_' &&
- p[0] != '_' && p[1] == ')') {
- q--;
- *q = '\0';
- p++;
- } else
- *q++ = *p;
- }
- }
-
- if (last_underscore)
- *q++ = '_';
-
- *q = '\0';
-
- return result;
-}
-
-static void
-emoticon_tool_button_emoticon_clicked_cb (EEmoticonToolButton *button,
- GtkWidget *emoticon_button)
-{
- button->priv->active_button = emoticon_button;
- e_emoticon_tool_button_popdown (button);
-}
-
-static gboolean
-emoticon_tool_button_emoticon_release_event_cb (EEmoticonToolButton *button,
- GdkEventButton *event,
- GtkButton *emoticon_button)
-{
- GtkStateFlags state;
-
- state = gtk_widget_get_state_flags (GTK_WIDGET (button));
-
- if (state != GTK_STATE_FLAG_NORMAL)
- gtk_button_clicked (emoticon_button);
-
- return FALSE;
-}
-
-static gboolean
-emoticon_tool_button_button_release_event_cb (EEmoticonToolButton *button,
- GdkEventButton *event)
-{
- GtkToggleToolButton *tool_button;
- GtkWidget *event_widget;
- gboolean popup_in_progress;
-
- tool_button = GTK_TOGGLE_TOOL_BUTTON (button);
- event_widget = gtk_get_event_widget ((GdkEvent *) event);
-
- popup_in_progress = button->priv->popup_in_progress;
- button->priv->popup_in_progress = FALSE;
-
- if (event_widget != GTK_WIDGET (button))
- goto popdown;
-
- if (popup_in_progress)
- return FALSE;
-
- if (gtk_toggle_tool_button_get_active (tool_button))
- goto popdown;
-
- return FALSE;
-
-popdown:
- e_emoticon_tool_button_popdown (button);
-
- return TRUE;
-}
-
-static void
-emoticon_tool_button_child_show_cb (EEmoticonToolButton *button)
-{
- button->priv->popup_shown = TRUE;
- g_object_notify (G_OBJECT (button), "popup-shown");
-}
-
-static void
-emoticon_tool_button_child_hide_cb (EEmoticonToolButton *button)
-{
- button->priv->popup_shown = FALSE;
- g_object_notify (G_OBJECT (button), "popup-shown");
-}
-
-static gboolean
-emoticon_tool_button_child_key_press_event_cb (EEmoticonToolButton *button,
- GdkEventKey *event)
-{
- GtkWidget *popover = button->priv->popover;
-
- return gtk_bindings_activate_event (G_OBJECT (popover), event) ||
- gtk_bindings_activate_event (G_OBJECT (button), event);
-}
-
-static void
-emoticon_tool_button_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- switch (property_id) {
- case PROP_CURRENT_EMOTICON:
- e_emoticon_chooser_set_current_emoticon (
- E_EMOTICON_CHOOSER (object),
- g_value_get_boxed (value));
- return;
-
- case PROP_POPUP_SHOWN:
- if (g_value_get_boolean (value))
- e_emoticon_tool_button_popup (
- E_EMOTICON_TOOL_BUTTON (object));
- else
- e_emoticon_tool_button_popdown (
- E_EMOTICON_TOOL_BUTTON (object));
- return;
- }
-
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-}
-
-static void
-emoticon_tool_button_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec)
-{
- EEmoticonToolButton *self = E_EMOTICON_TOOL_BUTTON (object);
-
- switch (property_id) {
- case PROP_CURRENT_EMOTICON:
- g_value_set_boxed (
- value,
- e_emoticon_chooser_get_current_emoticon (
- E_EMOTICON_CHOOSER (object)));
- return;
-
- case PROP_POPUP_SHOWN:
- g_value_set_boolean (value, self->priv->popup_shown);
- return;
- }
-
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-}
-
-static void
-emoticon_tool_button_dispose (GObject *object)
-{
- EEmoticonToolButton *self = E_EMOTICON_TOOL_BUTTON (object);
-
- g_clear_pointer (&self->priv->popover, gtk_widget_destroy);
-
- /* Chain up to parent's dispose() method. */
- G_OBJECT_CLASS (e_emoticon_tool_button_parent_class)->dispose (object);
-}
-
-static gboolean
-emoticon_tool_button_press_event (GtkWidget *widget,
- GdkEventButton *event)
-{
- EEmoticonToolButton *button;
- GtkToggleToolButton *toggle_button;
- GtkWidget *event_widget;
-
- button = E_EMOTICON_TOOL_BUTTON (widget);
-
- event_widget = gtk_get_event_widget ((GdkEvent *) event);
-
- if (event_widget == button->priv->popover)
- return TRUE;
-
- if (event_widget != widget)
- return FALSE;
-
- toggle_button = GTK_TOGGLE_TOOL_BUTTON (widget);
- if (gtk_toggle_tool_button_get_active (toggle_button))
- return FALSE;
-
- e_emoticon_tool_button_popup (button);
-
- button->priv->popup_in_progress = TRUE;
-
- return TRUE;
-}
-
-static void
-emoticon_tool_button_toggled (GtkToggleToolButton *button)
-{
- if (gtk_toggle_tool_button_get_active (button))
- e_emoticon_tool_button_popup (
- E_EMOTICON_TOOL_BUTTON (button));
- else
- e_emoticon_tool_button_popdown (
- E_EMOTICON_TOOL_BUTTON (button));
-}
-
-static void
-emoticon_tool_button_popup (EEmoticonToolButton *button)
-{
- GtkToggleToolButton *tool_button;
-
- if (!gtk_widget_get_realized (GTK_WIDGET (button)))
- return;
-
- if (button->priv->popup_shown)
- return;
-
- /* Activate the tool button. */
- tool_button = GTK_TOGGLE_TOOL_BUTTON (button);
- gtk_toggle_tool_button_set_active (tool_button, TRUE);
-
- /* Show the pop-up. */
- gtk_widget_show (button->priv->popover);
- gtk_widget_grab_focus (button->priv->table);
-}
-
-static void
-emoticon_tool_button_popdown (EEmoticonToolButton *button)
-{
- GtkToggleToolButton *tool_button;
-
- if (!gtk_widget_get_realized (GTK_WIDGET (button)))
- return;
-
- if (!button->priv->popup_shown)
- return;
-
- gtk_widget_hide (button->priv->popover);
-
- /* Deactivate the tool button. */
- tool_button = GTK_TOGGLE_TOOL_BUTTON (button);
- gtk_toggle_tool_button_set_active (tool_button, FALSE);
-}
-
-static EEmoticon *
-emoticon_tool_button_get_current_emoticon (EEmoticonChooser *chooser)
-{
- EEmoticonToolButton *self = E_EMOTICON_TOOL_BUTTON (chooser);
-
- if (!self->priv->active_button)
- return NULL;
-
- return g_object_get_data (G_OBJECT (self->priv->active_button), "emoticon");
-}
-
-static void
-emoticon_tool_button_set_current_emoticon (EEmoticonChooser *chooser,
- EEmoticon *emoticon)
-{
- EEmoticonToolButton *self = E_EMOTICON_TOOL_BUTTON (chooser);
- GList *list, *iter;
-
- list = gtk_container_get_children (GTK_CONTAINER (self->priv->table));
-
- for (iter = list; iter != NULL; iter = iter->next) {
- GtkWidget *item = iter->data;
- EEmoticon *candidate;
-
- candidate = g_object_get_data (G_OBJECT (item), "emoticon");
- if (candidate == NULL)
- continue;
-
- if (e_emoticon_equal (emoticon, candidate)) {
- gtk_button_clicked (GTK_BUTTON (item));
- break;
- }
- }
-
- g_list_free (list);
-}
-
-static void
-e_emoticon_tool_button_class_init (EEmoticonToolButtonClass *class)
-{
- GObjectClass *object_class;
- GtkWidgetClass *widget_class;
- GtkToggleToolButtonClass *toggle_tool_button_class;
-
- object_class = G_OBJECT_CLASS (class);
- object_class->set_property = emoticon_tool_button_set_property;
- object_class->get_property = emoticon_tool_button_get_property;
- object_class->dispose = emoticon_tool_button_dispose;
-
- widget_class = GTK_WIDGET_CLASS (class);
- widget_class->button_press_event = emoticon_tool_button_press_event;
-
- toggle_tool_button_class = GTK_TOGGLE_TOOL_BUTTON_CLASS (class);
- toggle_tool_button_class->toggled = emoticon_tool_button_toggled;
-
- class->popup = emoticon_tool_button_popup;
- class->popdown = emoticon_tool_button_popdown;
-
- g_object_class_override_property (
- object_class, PROP_CURRENT_EMOTICON, "current-emoticon");
-
- g_object_class_install_property (
- object_class,
- PROP_POPUP_SHOWN,
- g_param_spec_boolean (
- "popup-shown",
- "Popup Shown",
- "Whether the button's dropdown is shown",
- FALSE,
- G_PARAM_READWRITE));
-
- signals[POPUP] = g_signal_new (
- "popup",
- G_OBJECT_CLASS_TYPE (class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (EEmoticonToolButtonClass, popup),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
-
- signals[POPDOWN] = g_signal_new (
- "popdown",
- G_OBJECT_CLASS_TYPE (class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (EEmoticonToolButtonClass, popdown),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
-
- gtk_binding_entry_add_signal (
- gtk_binding_set_by_class (class),
- GDK_KEY_Down, GDK_MOD1_MASK, "popup", 0);
- gtk_binding_entry_add_signal (
- gtk_binding_set_by_class (class),
- GDK_KEY_KP_Down, GDK_MOD1_MASK, "popup", 0);
-
- gtk_binding_entry_add_signal (
- gtk_binding_set_by_class (class),
- GDK_KEY_Up, GDK_MOD1_MASK, "popdown", 0);
- gtk_binding_entry_add_signal (
- gtk_binding_set_by_class (class),
- GDK_KEY_KP_Up, GDK_MOD1_MASK, "popdown", 0);
- gtk_binding_entry_add_signal (
- gtk_binding_set_by_class (class),
- GDK_KEY_Escape, 0, "popdown", 0);
-}
-
-static void
-e_emoticon_tool_button_interface_init (EEmoticonChooserInterface *interface)
-{
- interface->get_current_emoticon =
- emoticon_tool_button_get_current_emoticon;
- interface->set_current_emoticon =
- emoticon_tool_button_set_current_emoticon;
-}
-
-static void
-e_emoticon_tool_button_init (EEmoticonToolButton *button)
-{
- EEmoticonChooser *chooser;
- GtkWidget *container;
- GtkWidget *widget;
- GtkWidget *popover;
- GList *list, *iter;
- gint ii;
-
- button->priv = e_emoticon_tool_button_get_instance_private (button);
-
- /* Build the popover. */
- popover = gtk_popover_new (GTK_WIDGET (button));
- gtk_popover_set_position (GTK_POPOVER (popover), GTK_POS_BOTTOM);
- gtk_popover_set_modal (GTK_POPOVER (popover), TRUE);
- button->priv->popover = g_object_ref_sink (popover);
-
- g_signal_connect_swapped (
- popover, "show",
- G_CALLBACK (emoticon_tool_button_child_show_cb), button);
- g_signal_connect_swapped (
- popover, "hide",
- G_CALLBACK (emoticon_tool_button_child_hide_cb), button);
- g_signal_connect_swapped (
- popover, "button-release-event",
- G_CALLBACK (emoticon_tool_button_button_release_event_cb),
- button);
- g_signal_connect_swapped (
- popover, "key-press-event",
- G_CALLBACK (emoticon_tool_button_child_key_press_event_cb),
- button);
-
- /* Build the popover content. */
- container = popover;
-
- widget = gtk_table_new (NUM_ROWS, NUM_COLS, TRUE);
- gtk_table_set_row_spacings (GTK_TABLE (widget), 0);
- gtk_table_set_col_spacings (GTK_TABLE (widget), 0);
- gtk_container_add (GTK_CONTAINER (container), widget);
- button->priv->table = g_object_ref (widget);
- gtk_widget_show (widget);
-
- container = widget;
-
- chooser = E_EMOTICON_CHOOSER (button);
- list = e_emoticon_chooser_get_items ();
- g_return_if_fail (g_list_length (list) <= NUM_ROWS * NUM_COLS);
-
- for (iter = list, ii = 0; iter != NULL; iter = iter->next, ii++) {
- EEmoticon *emoticon = iter->data;
- guint left = ii % NUM_COLS;
- guint top = ii / NUM_COLS;
- gchar *tooltip;
-
- tooltip = emoticon_tool_button_elide_underscores (
- gettext (emoticon->label));
-
- widget = gtk_button_new ();
- gtk_button_set_image (
- GTK_BUTTON (widget),
- gtk_image_new_from_icon_name (
- emoticon->icon_name, GTK_ICON_SIZE_BUTTON));
- gtk_button_set_relief (GTK_BUTTON (widget), GTK_RELIEF_NONE);
- gtk_widget_set_tooltip_text (widget, tooltip);
- gtk_widget_show (widget);
-
- g_object_set_data_full (
- G_OBJECT (widget), "emoticon",
- e_emoticon_copy (emoticon),
- (GDestroyNotify) e_emoticon_free);
-
- g_signal_connect_swapped (
- widget, "clicked",
- G_CALLBACK (emoticon_tool_button_emoticon_clicked_cb),
- button);
-
- g_signal_connect_swapped (
- widget, "clicked",
- G_CALLBACK (e_emoticon_chooser_item_activated),
- chooser);
-
- g_signal_connect_swapped (
- widget, "button-release-event",
- G_CALLBACK (emoticon_tool_button_emoticon_release_event_cb),
- button);
-
- gtk_table_attach (
- GTK_TABLE (container), widget,
- left, left + 1, top, top + 1, 0, 0, 0, 0);
-
- g_free (tooltip);
- }
-
- g_list_free (list);
-}
-
-GtkToolItem *
-e_emoticon_tool_button_new (void)
-{
- return g_object_new (E_TYPE_EMOTICON_TOOL_BUTTON, NULL);
-}
-
-void
-e_emoticon_tool_button_popup (EEmoticonToolButton *button)
-{
- g_return_if_fail (E_IS_EMOTICON_TOOL_BUTTON (button));
-
- g_signal_emit (button, signals[POPUP], 0);
-}
-
-void
-e_emoticon_tool_button_popdown (EEmoticonToolButton *button)
-{
- g_return_if_fail (E_IS_EMOTICON_TOOL_BUTTON (button));
-
- g_signal_emit (button, signals[POPDOWN], 0);
-}
diff --git a/src/e-util/e-emoticon-tool-button.h b/src/e-util/e-emoticon-tool-button.h
deleted file mode 100644
index fc7dc8ef64..0000000000
--- a/src/e-util/e-emoticon-tool-button.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * e-emoticon-tool-button.h
- *
- * Copyright (C) 2008 Novell, Inc.
- * Copyright (C) 2012 Dan Vrátil
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU Lesser General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
-#error "Only should be included directly."
-#endif
-
-#ifndef E_EMOTICON_TOOL_BUTTON_H
-#define E_EMOTICON_TOOL_BUTTON_H
-
-#include
-
-/* Standard GObject macros */
-#define E_TYPE_EMOTICON_TOOL_BUTTON \
- (e_emoticon_tool_button_get_type ())
-#define E_EMOTICON_TOOL_BUTTON(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST \
- ((obj), E_TYPE_EMOTICON_TOOL_BUTTON, EEmoticonToolButton))
-#define E_EMOTICON_TOOL_BUTTON_CLASS(cls) \
- (G_TYPE_CHECK_CLASS_CAST \
- ((cls), E_TYPE_EMOTICON_TOOL_BUTTON, EEmoticonToolButtonClass))
-#define E_IS_EMOTICON_TOOL_BUTTON(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE \
- ((obj), E_TYPE_EMOTICON_TOOL_BUTTON))
-#define E_IS_EMOTICON_TOOL_BUTTON_CLASS(cls) \
- (G_TYPE_CHECK_CLASS_TYPE \
- ((cls), E_TYPE_EMOTICON_TOOL_BUTTON))
-#define E_EMOTICON_TOOL_BUTTON_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS \
- ((obj), E_TYPE_EMOTICON_TOOL_BUTTON, EEmoticonToolButtonClass))
-
-G_BEGIN_DECLS
-
-typedef struct _EEmoticonToolButton EEmoticonToolButton;
-typedef struct _EEmoticonToolButtonClass EEmoticonToolButtonClass;
-typedef struct _EEmoticonToolButtonPrivate EEmoticonToolButtonPrivate;
-
-struct _EEmoticonToolButton {
- GtkToggleToolButton parent;
- EEmoticonToolButtonPrivate *priv;
-};
-
-struct _EEmoticonToolButtonClass {
- GtkToggleToolButtonClass parent_class;
-
- void (*popup) (EEmoticonToolButton *button);
- void (*popdown) (EEmoticonToolButton *button);
-};
-
-GType e_emoticon_tool_button_get_type (void) G_GNUC_CONST;
-GtkToolItem * e_emoticon_tool_button_new (void);
-void e_emoticon_tool_button_popup (EEmoticonToolButton *button);
-void e_emoticon_tool_button_popdown (EEmoticonToolButton *button);
-
-G_END_DECLS
-
-#endif /* E_EMOTICON_TOOL_BUTTON_H */
diff --git a/src/e-util/e-emoticon.c b/src/e-util/e-emoticon.c
index 53a9f494f6..8082e7238b 100644
--- a/src/e-util/e-emoticon.c
+++ b/src/e-util/e-emoticon.c
@@ -24,7 +24,7 @@
#include
static EEmoticon *
-emoticon_copy (EEmoticon *emoticon)
+emoticon_copy (const EEmoticon *emoticon)
{
EEmoticon *copy;
@@ -62,8 +62,8 @@ e_emoticon_get_type (void)
}
gboolean
-e_emoticon_equal (EEmoticon *emoticon_a,
- EEmoticon *emoticon_b)
+e_emoticon_equal (const EEmoticon *emoticon_a,
+ const EEmoticon *emoticon_b)
{
if (((emoticon_a == NULL) && (emoticon_b != NULL)) ||
((emoticon_a != NULL) && (emoticon_b == NULL)))
@@ -88,7 +88,7 @@ e_emoticon_equal (EEmoticon *emoticon_a,
}
EEmoticon *
-e_emoticon_copy (EEmoticon *emoticon)
+e_emoticon_copy (const EEmoticon *emoticon)
{
return g_boxed_copy (E_TYPE_EMOTICON, emoticon);
}
@@ -100,7 +100,7 @@ e_emoticon_free (EEmoticon *emoticon)
}
gchar *
-e_emoticon_get_uri (EEmoticon *emoticon)
+e_emoticon_dup_uri (const EEmoticon *emoticon)
{
GtkIconInfo *icon_info;
GtkIconTheme *icon_theme;
@@ -123,7 +123,7 @@ e_emoticon_get_uri (EEmoticon *emoticon)
}
const gchar *
-e_emoticon_get_name (EEmoticon *emoticon)
+e_emoticon_get_name (const EEmoticon *emoticon)
{
return emoticon->icon_name;
}
diff --git a/src/e-util/e-emoticon.h b/src/e-util/e-emoticon.h
index 3b9e8c0a8f..0d4a90a609 100644
--- a/src/e-util/e-emoticon.h
+++ b/src/e-util/e-emoticon.h
@@ -43,12 +43,12 @@ struct _EEmoticon {
};
GType e_emoticon_get_type (void) G_GNUC_CONST;
-gboolean e_emoticon_equal (EEmoticon *emoticon_a,
- EEmoticon *emoticon_b);
-EEmoticon * e_emoticon_copy (EEmoticon *emoticon);
+gboolean e_emoticon_equal (const EEmoticon *emoticon_a,
+ const EEmoticon *emoticon_b);
+EEmoticon * e_emoticon_copy (const EEmoticon *emoticon);
void e_emoticon_free (EEmoticon *emoticon);
-gchar * e_emoticon_get_uri (EEmoticon *emoticon);
-const gchar * e_emoticon_get_name (EEmoticon *emoticon);
+gchar * e_emoticon_dup_uri (const EEmoticon *emoticon);
+const gchar * e_emoticon_get_name (const EEmoticon *emoticon);
G_END_DECLS
diff --git a/src/e-util/e-focus-tracker.c b/src/e-util/e-focus-tracker.c
index e742da3c32..e73c7a77f9 100644
--- a/src/e-util/e-focus-tracker.c
+++ b/src/e-util/e-focus-tracker.c
@@ -20,25 +20,52 @@
#include "evolution-config.h"
-#include "e-focus-tracker.h"
-
#include
-#include "e-selectable.h"
-#include "e-widget-undo.h"
#include "e-content-editor.h"
+#include "e-selectable.h"
+#include "e-ui-action.h"
+#include "e-widget-undo.h"
+
+#include "e-focus-tracker.h"
+
+#define disconnect_and_unref(_x) G_STMT_START { \
+ if (_x != NULL) { \
+ g_signal_handlers_disconnect_matched ( \
+ _x, G_SIGNAL_MATCH_DATA, \
+ 0, 0, NULL, NULL, focus_tracker); \
+ g_clear_object (&(_x)); \
+ } \
+} G_STMT_END
+
+#define replace_action(_priv_member, _arg, _cb, _prop_name) G_STMT_START { \
+ g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker)); \
+ if (_arg != NULL) { \
+ g_return_if_fail (E_IS_UI_ACTION (_arg)); \
+ g_object_ref (_arg); \
+ } \
+ disconnect_and_unref (_priv_member); \
+ _priv_member = _arg; \
+ if (_arg != NULL) { \
+ g_signal_connect_swapped ( \
+ _arg, "activate", \
+ G_CALLBACK (_cb), \
+ focus_tracker); \
+ } \
+ g_object_notify (G_OBJECT (focus_tracker), _prop_name); \
+} G_STMT_END
struct _EFocusTrackerPrivate {
GtkWidget *focus; /* not referenced */
GtkWindow *window;
- GtkAction *cut_clipboard;
- GtkAction *copy_clipboard;
- GtkAction *paste_clipboard;
- GtkAction *delete_selection;
- GtkAction *select_all;
- GtkAction *undo;
- GtkAction *redo;
+ EUIAction *cut_clipboard;
+ EUIAction *copy_clipboard;
+ EUIAction *paste_clipboard;
+ EUIAction *delete_selection;
+ EUIAction *select_all;
+ EUIAction *undo;
+ EUIAction *redo;
};
enum {
@@ -59,35 +86,35 @@ G_DEFINE_TYPE_WITH_PRIVATE (EFocusTracker, e_focus_tracker, G_TYPE_OBJECT)
static void
focus_tracker_disable_actions (EFocusTracker *focus_tracker)
{
- GtkAction *action;
+ EUIAction *action;
action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
if (action != NULL)
- gtk_action_set_sensitive (action, FALSE);
+ e_ui_action_set_sensitive (action, FALSE);
action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
if (action != NULL)
- gtk_action_set_sensitive (action, FALSE);
+ e_ui_action_set_sensitive (action, FALSE);
action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
if (action != NULL)
- gtk_action_set_sensitive (action, FALSE);
+ e_ui_action_set_sensitive (action, FALSE);
action = e_focus_tracker_get_delete_selection_action (focus_tracker);
if (action != NULL)
- gtk_action_set_sensitive (action, FALSE);
+ e_ui_action_set_sensitive (action, FALSE);
action = e_focus_tracker_get_select_all_action (focus_tracker);
if (action != NULL)
- gtk_action_set_sensitive (action, FALSE);
+ e_ui_action_set_sensitive (action, FALSE);
action = e_focus_tracker_get_undo_action (focus_tracker);
if (action != NULL)
- gtk_action_set_sensitive (action, FALSE);
+ e_ui_action_set_sensitive (action, FALSE);
action = e_focus_tracker_get_redo_action (focus_tracker);
if (action != NULL)
- gtk_action_set_sensitive (action, FALSE);
+ e_ui_action_set_sensitive (action, FALSE);
}
static gboolean
@@ -125,38 +152,38 @@ focus_tracker_update_undo_redo (EFocusTracker *focus_tracker,
GtkWidget *widget,
gboolean can_edit_text)
{
- GtkAction *action;
+ EUIAction *action;
gboolean sensitive;
action = e_focus_tracker_get_undo_action (focus_tracker);
if (action != NULL) {
sensitive = can_edit_text && focus_tracker_get_has_undo (widget);
- gtk_action_set_sensitive (action, sensitive);
+ e_ui_action_set_sensitive (action, sensitive);
if (sensitive) {
gchar *description;
description = e_widget_undo_describe_undo (widget);
- gtk_action_set_tooltip (action, description && *description ? description : _("Undo"));
+ e_ui_action_set_tooltip (action, description && *description ? description : _("Undo"));
g_free (description);
} else {
- gtk_action_set_tooltip (action, _("Undo"));
+ e_ui_action_set_tooltip (action, _("Undo"));
}
}
action = e_focus_tracker_get_redo_action (focus_tracker);
if (action != NULL) {
sensitive = can_edit_text && focus_tracker_get_has_redo (widget);
- gtk_action_set_sensitive (action, sensitive);
+ e_ui_action_set_sensitive (action, sensitive);
if (sensitive) {
gchar *description;
description = e_widget_undo_describe_redo (widget);
- gtk_action_set_tooltip (action, description && *description ? description : _("Redo"));
+ e_ui_action_set_tooltip (action, description && *description ? description : _("Redo"));
g_free (description);
} else {
- gtk_action_set_tooltip (action, _("Redo"));
+ e_ui_action_set_tooltip (action, _("Redo"));
}
}
}
@@ -167,7 +194,7 @@ focus_tracker_editable_update_actions (EFocusTracker *focus_tracker,
GdkAtom *targets,
gint n_targets)
{
- GtkAction *action;
+ EUIAction *action;
gboolean can_edit_text;
gboolean clipboard_has_text;
gboolean text_is_selected;
@@ -185,36 +212,36 @@ focus_tracker_editable_update_actions (EFocusTracker *focus_tracker,
action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
if (action != NULL) {
sensitive = can_edit_text && text_is_selected;
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, _("Cut the selection"));
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, _("Cut the selection"));
}
action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
if (action != NULL) {
sensitive = text_is_selected;
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, _("Copy the selection"));
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, _("Copy the selection"));
}
action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
if (action != NULL) {
sensitive = can_edit_text && clipboard_has_text;
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, _("Paste the clipboard"));
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, _("Paste the clipboard"));
}
action = e_focus_tracker_get_delete_selection_action (focus_tracker);
if (action != NULL) {
sensitive = can_edit_text && text_is_selected;
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, _("Delete the selection"));
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, _("Delete the selection"));
}
action = e_focus_tracker_get_select_all_action (focus_tracker);
if (action != NULL) {
sensitive = TRUE; /* always enabled */
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, _("Select all text"));
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, _("Select all text"));
}
focus_tracker_update_undo_redo (focus_tracker, GTK_WIDGET (editable), can_edit_text);
@@ -226,7 +253,7 @@ focus_tracker_text_view_update_actions (EFocusTracker *focus_tracker,
GdkAtom *targets,
gint n_targets)
{
- GtkAction *action;
+ EUIAction *action;
GtkTextBuffer *buffer;
gboolean can_edit_text;
gboolean clipboard_has_text;
@@ -241,36 +268,36 @@ focus_tracker_text_view_update_actions (EFocusTracker *focus_tracker,
action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
if (action != NULL) {
sensitive = can_edit_text && text_is_selected;
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, _("Cut the selection"));
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, _("Cut the selection"));
}
action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
if (action != NULL) {
sensitive = text_is_selected;
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, _("Copy the selection"));
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, _("Copy the selection"));
}
action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
if (action != NULL) {
sensitive = can_edit_text && clipboard_has_text;
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, _("Paste the clipboard"));
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, _("Paste the clipboard"));
}
action = e_focus_tracker_get_delete_selection_action (focus_tracker);
if (action != NULL) {
sensitive = can_edit_text && text_is_selected;
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, _("Delete the selection"));
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, _("Delete the selection"));
}
action = e_focus_tracker_get_select_all_action (focus_tracker);
if (action != NULL) {
sensitive = TRUE; /* always enabled */
- gtk_action_set_sensitive (action, sensitive);
- gtk_action_set_tooltip (action, _("Select all text"));
+ e_ui_action_set_sensitive (action, sensitive);
+ e_ui_action_set_tooltip (action, _("Select all text"));
}
focus_tracker_update_undo_redo (focus_tracker, GTK_WIDGET (text_view), can_edit_text);
@@ -282,7 +309,7 @@ focus_tracker_editor_update_actions (EFocusTracker *focus_tracker,
GdkAtom *targets,
gint n_targets)
{
- GtkAction *action;
+ EUIAction *action;
gboolean can_copy;
gboolean can_cut;
gboolean can_paste;
@@ -295,20 +322,20 @@ focus_tracker_editor_update_actions (EFocusTracker *focus_tracker,
action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
if (action != NULL) {
- gtk_action_set_sensitive (action, can_cut);
- gtk_action_set_tooltip (action, _("Cut the selection"));
+ e_ui_action_set_sensitive (action, can_cut);
+ e_ui_action_set_tooltip (action, _("Cut the selection"));
}
action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
if (action != NULL) {
- gtk_action_set_sensitive (action, can_copy);
- gtk_action_set_tooltip (action, _("Copy the selection"));
+ e_ui_action_set_sensitive (action, can_copy);
+ e_ui_action_set_tooltip (action, _("Copy the selection"));
}
action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
if (action != NULL) {
- gtk_action_set_sensitive (action, can_paste);
- gtk_action_set_tooltip (action, _("Paste the clipboard"));
+ e_ui_action_set_sensitive (action, can_paste);
+ e_ui_action_set_tooltip (action, _("Paste the clipboard"));
}
focus_tracker_update_undo_redo (focus_tracker, GTK_WIDGET (cnt_editor),
@@ -322,7 +349,7 @@ focus_tracker_selectable_update_actions (EFocusTracker *focus_tracker,
gint n_targets)
{
ESelectableInterface *iface;
- GtkAction *action;
+ EUIAction *action;
iface = E_SELECTABLE_GET_INTERFACE (selectable);
@@ -337,31 +364,31 @@ focus_tracker_selectable_update_actions (EFocusTracker *focus_tracker,
action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
if (action != NULL && iface->cut_clipboard == NULL)
- gtk_action_set_sensitive (action, FALSE);
+ e_ui_action_set_sensitive (action, FALSE);
action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
if (action != NULL && iface->copy_clipboard == NULL)
- gtk_action_set_sensitive (action, FALSE);
+ e_ui_action_set_sensitive (action, FALSE);
action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
if (action != NULL && iface->paste_clipboard == NULL)
- gtk_action_set_sensitive (action, FALSE);
+ e_ui_action_set_sensitive (action, FALSE);
action = e_focus_tracker_get_delete_selection_action (focus_tracker);
if (action != NULL && iface->delete_selection == NULL)
- gtk_action_set_sensitive (action, FALSE);
+ e_ui_action_set_sensitive (action, FALSE);
action = e_focus_tracker_get_select_all_action (focus_tracker);
if (action != NULL && iface->select_all == NULL)
- gtk_action_set_sensitive (action, FALSE);
+ e_ui_action_set_sensitive (action, FALSE);
action = e_focus_tracker_get_undo_action (focus_tracker);
if (action != NULL && iface->undo == NULL)
- gtk_action_set_sensitive (action, FALSE);
+ e_ui_action_set_sensitive (action, FALSE);
action = e_focus_tracker_get_redo_action (focus_tracker);
if (action != NULL && iface->redo == NULL)
- gtk_action_set_sensitive (action, FALSE);
+ e_ui_action_set_sensitive (action, FALSE);
}
static void
@@ -589,57 +616,24 @@ focus_tracker_get_property (GObject *object,
static void
focus_tracker_dispose (GObject *object)
{
- EFocusTracker *self = E_FOCUS_TRACKER (object);
+ EFocusTracker *focus_tracker = E_FOCUS_TRACKER (object);
g_signal_handlers_disconnect_matched (
gtk_clipboard_get (GDK_SELECTION_PRIMARY),
- G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, object);
+ G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, focus_tracker);
g_signal_handlers_disconnect_matched (
gtk_clipboard_get (GDK_SELECTION_CLIPBOARD),
- G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, object);
+ G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, focus_tracker);
- if (self->priv->window != NULL) {
- g_signal_handlers_disconnect_matched (
- self->priv->window, G_SIGNAL_MATCH_DATA,
- 0, 0, NULL, NULL, object);
- g_clear_object (&self->priv->window);
- }
-
- if (self->priv->cut_clipboard != NULL) {
- g_signal_handlers_disconnect_matched (
- self->priv->cut_clipboard, G_SIGNAL_MATCH_DATA,
- 0, 0, NULL, NULL, object);
- g_clear_object (&self->priv->cut_clipboard);
- }
-
- if (self->priv->copy_clipboard != NULL) {
- g_signal_handlers_disconnect_matched (
- self->priv->copy_clipboard, G_SIGNAL_MATCH_DATA,
- 0, 0, NULL, NULL, object);
- g_clear_object (&self->priv->copy_clipboard);
- }
-
- if (self->priv->paste_clipboard != NULL) {
- g_signal_handlers_disconnect_matched (
- self->priv->paste_clipboard, G_SIGNAL_MATCH_DATA,
- 0, 0, NULL, NULL, object);
- g_clear_object (&self->priv->paste_clipboard);
- }
-
- if (self->priv->delete_selection != NULL) {
- g_signal_handlers_disconnect_matched (
- self->priv->delete_selection, G_SIGNAL_MATCH_DATA,
- 0, 0, NULL, NULL, object);
- g_clear_object (&self->priv->delete_selection);
- }
-
- if (self->priv->select_all != NULL) {
- g_signal_handlers_disconnect_matched (
- self->priv->select_all, G_SIGNAL_MATCH_DATA,
- 0, 0, NULL, NULL, object);
- g_clear_object (&self->priv->select_all);
- }
+ disconnect_and_unref (focus_tracker->priv->window);
+ disconnect_and_unref (focus_tracker->priv->cut_clipboard);
+ disconnect_and_unref (focus_tracker->priv->copy_clipboard);
+ disconnect_and_unref (focus_tracker->priv->paste_clipboard);
+ disconnect_and_unref (focus_tracker->priv->delete_selection);
+ disconnect_and_unref (focus_tracker->priv->select_all);
+ disconnect_and_unref (focus_tracker->priv->undo);
+ disconnect_and_unref (focus_tracker->priv->redo);
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (e_focus_tracker_parent_class)->dispose (object);
@@ -714,7 +708,7 @@ e_focus_tracker_class_init (EFocusTrackerClass *class)
"cut-clipboard-action",
"Cut Clipboard Action",
NULL,
- GTK_TYPE_ACTION,
+ E_TYPE_UI_ACTION,
G_PARAM_READWRITE));
g_object_class_install_property (
@@ -724,7 +718,7 @@ e_focus_tracker_class_init (EFocusTrackerClass *class)
"copy-clipboard-action",
"Copy Clipboard Action",
NULL,
- GTK_TYPE_ACTION,
+ E_TYPE_UI_ACTION,
G_PARAM_READWRITE));
g_object_class_install_property (
@@ -734,7 +728,7 @@ e_focus_tracker_class_init (EFocusTrackerClass *class)
"paste-clipboard-action",
"Paste Clipboard Action",
NULL,
- GTK_TYPE_ACTION,
+ E_TYPE_UI_ACTION,
G_PARAM_READWRITE));
g_object_class_install_property (
@@ -744,7 +738,7 @@ e_focus_tracker_class_init (EFocusTrackerClass *class)
"delete-selection-action",
"Delete Selection Action",
NULL,
- GTK_TYPE_ACTION,
+ E_TYPE_UI_ACTION,
G_PARAM_READWRITE));
g_object_class_install_property (
@@ -754,7 +748,7 @@ e_focus_tracker_class_init (EFocusTrackerClass *class)
"select-all-action",
"Select All Action",
NULL,
- GTK_TYPE_ACTION,
+ E_TYPE_UI_ACTION,
G_PARAM_READWRITE));
g_object_class_install_property (
@@ -764,7 +758,7 @@ e_focus_tracker_class_init (EFocusTrackerClass *class)
"undo-action",
"Undo Action",
NULL,
- GTK_TYPE_ACTION,
+ E_TYPE_UI_ACTION,
G_PARAM_READWRITE));
g_object_class_install_property (
@@ -774,45 +768,14 @@ e_focus_tracker_class_init (EFocusTrackerClass *class)
"redo-action",
"Redo Action",
NULL,
- GTK_TYPE_ACTION,
+ E_TYPE_UI_ACTION,
G_PARAM_READWRITE));
}
static void
e_focus_tracker_init (EFocusTracker *focus_tracker)
{
- GtkAction *action;
-
focus_tracker->priv = e_focus_tracker_get_instance_private (focus_tracker);
-
- /* Define dummy actions. These will most likely be overridden,
- * but for cases where they're not it ensures ESelectable objects
- * will always get a valid GtkAction when they ask us for one. */
-
- action = gtk_action_new (
- "cut-clipboard", _("Cu_t"),
- _("Cut the selection"), "edit-cut");
- focus_tracker->priv->cut_clipboard = action;
-
- action = gtk_action_new (
- "copy-clipboard", _("_Copy"),
- _("Copy the selection"), "edit-copy");
- focus_tracker->priv->copy_clipboard = action;
-
- action = gtk_action_new (
- "paste-clipboard", _("_Paste"),
- _("Paste the clipboard"), "edit-paste");
- focus_tracker->priv->paste_clipboard = action;
-
- action = gtk_action_new (
- "delete-selection", _("_Delete"),
- _("Delete the selection"), "edit-delete");
- focus_tracker->priv->delete_selection = action;
-
- action = gtk_action_new (
- "select-all", _("Select _All"),
- _("Select all text"), "edit-select-all");
- focus_tracker->priv->select_all = action;
}
EFocusTracker *
@@ -839,7 +802,7 @@ e_focus_tracker_get_window (EFocusTracker *focus_tracker)
return focus_tracker->priv->window;
}
-GtkAction *
+EUIAction *
e_focus_tracker_get_cut_clipboard_action (EFocusTracker *focus_tracker)
{
g_return_val_if_fail (E_IS_FOCUS_TRACKER (focus_tracker), NULL);
@@ -849,35 +812,12 @@ e_focus_tracker_get_cut_clipboard_action (EFocusTracker *focus_tracker)
void
e_focus_tracker_set_cut_clipboard_action (EFocusTracker *focus_tracker,
- GtkAction *cut_clipboard)
+ EUIAction *cut_clipboard)
{
- g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
-
- if (cut_clipboard != NULL) {
- g_return_if_fail (GTK_IS_ACTION (cut_clipboard));
- g_object_ref (cut_clipboard);
- }
-
- if (focus_tracker->priv->cut_clipboard != NULL) {
- g_signal_handlers_disconnect_matched (
- focus_tracker->priv->cut_clipboard,
- G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
- focus_tracker);
- g_object_unref (focus_tracker->priv->cut_clipboard);
- }
-
- focus_tracker->priv->cut_clipboard = cut_clipboard;
-
- if (cut_clipboard != NULL)
- g_signal_connect_swapped (
- cut_clipboard, "activate",
- G_CALLBACK (e_focus_tracker_cut_clipboard),
- focus_tracker);
-
- g_object_notify (G_OBJECT (focus_tracker), "cut-clipboard-action");
+ replace_action (focus_tracker->priv->cut_clipboard, cut_clipboard, e_focus_tracker_cut_clipboard, "cut-clipboard-action");
}
-GtkAction *
+EUIAction *
e_focus_tracker_get_copy_clipboard_action (EFocusTracker *focus_tracker)
{
g_return_val_if_fail (E_IS_FOCUS_TRACKER (focus_tracker), NULL);
@@ -887,35 +827,12 @@ e_focus_tracker_get_copy_clipboard_action (EFocusTracker *focus_tracker)
void
e_focus_tracker_set_copy_clipboard_action (EFocusTracker *focus_tracker,
- GtkAction *copy_clipboard)
+ EUIAction *copy_clipboard)
{
- g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
-
- if (copy_clipboard != NULL) {
- g_return_if_fail (GTK_IS_ACTION (copy_clipboard));
- g_object_ref (copy_clipboard);
- }
-
- if (focus_tracker->priv->copy_clipboard != NULL) {
- g_signal_handlers_disconnect_matched (
- focus_tracker->priv->copy_clipboard,
- G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
- focus_tracker);
- g_object_unref (focus_tracker->priv->copy_clipboard);
- }
-
- focus_tracker->priv->copy_clipboard = copy_clipboard;
-
- if (copy_clipboard != NULL)
- g_signal_connect_swapped (
- copy_clipboard, "activate",
- G_CALLBACK (e_focus_tracker_copy_clipboard),
- focus_tracker);
-
- g_object_notify (G_OBJECT (focus_tracker), "copy-clipboard-action");
+ replace_action (focus_tracker->priv->copy_clipboard, copy_clipboard, e_focus_tracker_copy_clipboard, "copy-clipboard-action");
}
-GtkAction *
+EUIAction *
e_focus_tracker_get_paste_clipboard_action (EFocusTracker *focus_tracker)
{
g_return_val_if_fail (E_IS_FOCUS_TRACKER (focus_tracker), NULL);
@@ -925,35 +842,12 @@ e_focus_tracker_get_paste_clipboard_action (EFocusTracker *focus_tracker)
void
e_focus_tracker_set_paste_clipboard_action (EFocusTracker *focus_tracker,
- GtkAction *paste_clipboard)
+ EUIAction *paste_clipboard)
{
- g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
-
- if (paste_clipboard != NULL) {
- g_return_if_fail (GTK_IS_ACTION (paste_clipboard));
- g_object_ref (paste_clipboard);
- }
-
- if (focus_tracker->priv->paste_clipboard != NULL) {
- g_signal_handlers_disconnect_matched (
- focus_tracker->priv->paste_clipboard,
- G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
- focus_tracker);
- g_object_unref (focus_tracker->priv->paste_clipboard);
- }
-
- focus_tracker->priv->paste_clipboard = paste_clipboard;
-
- if (paste_clipboard != NULL)
- g_signal_connect_swapped (
- paste_clipboard, "activate",
- G_CALLBACK (e_focus_tracker_paste_clipboard),
- focus_tracker);
-
- g_object_notify (G_OBJECT (focus_tracker), "paste-clipboard-action");
+ replace_action (focus_tracker->priv->paste_clipboard, paste_clipboard, e_focus_tracker_paste_clipboard, "paste-clipboard-action");
}
-GtkAction *
+EUIAction *
e_focus_tracker_get_delete_selection_action (EFocusTracker *focus_tracker)
{
g_return_val_if_fail (E_IS_FOCUS_TRACKER (focus_tracker), NULL);
@@ -963,35 +857,12 @@ e_focus_tracker_get_delete_selection_action (EFocusTracker *focus_tracker)
void
e_focus_tracker_set_delete_selection_action (EFocusTracker *focus_tracker,
- GtkAction *delete_selection)
+ EUIAction *delete_selection)
{
- g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
-
- if (delete_selection != NULL) {
- g_return_if_fail (GTK_IS_ACTION (delete_selection));
- g_object_ref (delete_selection);
- }
-
- if (focus_tracker->priv->delete_selection != NULL) {
- g_signal_handlers_disconnect_matched (
- focus_tracker->priv->delete_selection,
- G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
- focus_tracker);
- g_object_unref (focus_tracker->priv->delete_selection);
- }
-
- focus_tracker->priv->delete_selection = delete_selection;
-
- if (delete_selection != NULL)
- g_signal_connect_swapped (
- delete_selection, "activate",
- G_CALLBACK (e_focus_tracker_delete_selection),
- focus_tracker);
-
- g_object_notify (G_OBJECT (focus_tracker), "delete-selection-action");
+ replace_action (focus_tracker->priv->delete_selection, delete_selection, e_focus_tracker_delete_selection, "delete-selection-action");
}
-GtkAction *
+EUIAction *
e_focus_tracker_get_select_all_action (EFocusTracker *focus_tracker)
{
g_return_val_if_fail (E_IS_FOCUS_TRACKER (focus_tracker), NULL);
@@ -1001,35 +872,12 @@ e_focus_tracker_get_select_all_action (EFocusTracker *focus_tracker)
void
e_focus_tracker_set_select_all_action (EFocusTracker *focus_tracker,
- GtkAction *select_all)
+ EUIAction *select_all)
{
- g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
-
- if (select_all != NULL) {
- g_return_if_fail (GTK_IS_ACTION (select_all));
- g_object_ref (select_all);
- }
-
- if (focus_tracker->priv->select_all != NULL) {
- g_signal_handlers_disconnect_matched (
- focus_tracker->priv->select_all,
- G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
- focus_tracker);
- g_object_unref (focus_tracker->priv->select_all);
- }
-
- focus_tracker->priv->select_all = select_all;
-
- if (select_all != NULL)
- g_signal_connect_swapped (
- select_all, "activate",
- G_CALLBACK (e_focus_tracker_select_all),
- focus_tracker);
-
- g_object_notify (G_OBJECT (focus_tracker), "select-all-action");
+ replace_action (focus_tracker->priv->select_all, select_all, e_focus_tracker_select_all, "select-all-action");
}
-GtkAction *
+EUIAction *
e_focus_tracker_get_undo_action (EFocusTracker *focus_tracker)
{
g_return_val_if_fail (E_IS_FOCUS_TRACKER (focus_tracker), NULL);
@@ -1039,35 +887,12 @@ e_focus_tracker_get_undo_action (EFocusTracker *focus_tracker)
void
e_focus_tracker_set_undo_action (EFocusTracker *focus_tracker,
- GtkAction *undo)
+ EUIAction *undo)
{
- g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
-
- if (undo != NULL) {
- g_return_if_fail (GTK_IS_ACTION (undo));
- g_object_ref (undo);
- }
-
- if (focus_tracker->priv->undo != NULL) {
- g_signal_handlers_disconnect_matched (
- focus_tracker->priv->undo,
- G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
- focus_tracker);
- g_object_unref (focus_tracker->priv->undo);
- }
-
- focus_tracker->priv->undo = undo;
-
- if (undo != NULL)
- g_signal_connect_swapped (
- undo, "activate",
- G_CALLBACK (e_focus_tracker_undo),
- focus_tracker);
-
- g_object_notify (G_OBJECT (focus_tracker), "undo-action");
+ replace_action (focus_tracker->priv->undo, undo, e_focus_tracker_undo, "undo-action");
}
-GtkAction *
+EUIAction *
e_focus_tracker_get_redo_action (EFocusTracker *focus_tracker)
{
g_return_val_if_fail (E_IS_FOCUS_TRACKER (focus_tracker), NULL);
@@ -1077,32 +902,9 @@ e_focus_tracker_get_redo_action (EFocusTracker *focus_tracker)
void
e_focus_tracker_set_redo_action (EFocusTracker *focus_tracker,
- GtkAction *redo)
+ EUIAction *redo)
{
- g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
-
- if (redo != NULL) {
- g_return_if_fail (GTK_IS_ACTION (redo));
- g_object_ref (redo);
- }
-
- if (focus_tracker->priv->redo != NULL) {
- g_signal_handlers_disconnect_matched (
- focus_tracker->priv->redo,
- G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
- focus_tracker);
- g_object_unref (focus_tracker->priv->redo);
- }
-
- focus_tracker->priv->redo = redo;
-
- if (redo != NULL)
- g_signal_connect_swapped (
- redo, "activate",
- G_CALLBACK (e_focus_tracker_redo),
- focus_tracker);
-
- g_object_notify (G_OBJECT (focus_tracker), "redo-action");
+ replace_action (focus_tracker->priv->redo, redo, e_focus_tracker_redo, "redo-action");
}
void
diff --git a/src/e-util/e-focus-tracker.h b/src/e-util/e-focus-tracker.h
index a4e5a4e210..f35f2e82f7 100644
--- a/src/e-util/e-focus-tracker.h
+++ b/src/e-util/e-focus-tracker.h
@@ -27,6 +27,8 @@
#include
+#include
+
/* Standard GObject macros */
#define E_TYPE_FOCUS_TRACKER \
(e_focus_tracker_get_type ())
@@ -65,37 +67,37 @@ GType e_focus_tracker_get_type (void) G_GNUC_CONST;
EFocusTracker * e_focus_tracker_new (GtkWindow *window);
GtkWidget * e_focus_tracker_get_focus (EFocusTracker *focus_tracker);
GtkWindow * e_focus_tracker_get_window (EFocusTracker *focus_tracker);
-GtkAction * e_focus_tracker_get_cut_clipboard_action
+EUIAction * e_focus_tracker_get_cut_clipboard_action
(EFocusTracker *focus_tracker);
void e_focus_tracker_set_cut_clipboard_action
(EFocusTracker *focus_tracker,
- GtkAction *cut_clipboard);
-GtkAction * e_focus_tracker_get_copy_clipboard_action
+ EUIAction *cut_clipboard);
+EUIAction * e_focus_tracker_get_copy_clipboard_action
(EFocusTracker *focus_tracker);
void e_focus_tracker_set_copy_clipboard_action
(EFocusTracker *focus_tracker,
- GtkAction *copy_clipboard);
-GtkAction * e_focus_tracker_get_paste_clipboard_action
+ EUIAction *copy_clipboard);
+EUIAction * e_focus_tracker_get_paste_clipboard_action
(EFocusTracker *focus_tracker);
void e_focus_tracker_set_paste_clipboard_action
(EFocusTracker *focus_tracker,
- GtkAction *paste_clipboard);
-GtkAction * e_focus_tracker_get_delete_selection_action
+ EUIAction *paste_clipboard);
+EUIAction * e_focus_tracker_get_delete_selection_action
(EFocusTracker *focus_tracker);
void e_focus_tracker_set_delete_selection_action
(EFocusTracker *focus_tracker,
- GtkAction *delete_selection);
-GtkAction * e_focus_tracker_get_select_all_action
+ EUIAction *delete_selection);
+EUIAction * e_focus_tracker_get_select_all_action
(EFocusTracker *focus_tracker);
void e_focus_tracker_set_select_all_action
(EFocusTracker *focus_tracker,
- GtkAction *select_all);
-GtkAction * e_focus_tracker_get_undo_action (EFocusTracker *focus_tracker);
+ EUIAction *select_all);
+EUIAction * e_focus_tracker_get_undo_action (EFocusTracker *focus_tracker);
void e_focus_tracker_set_undo_action (EFocusTracker *focus_tracker,
- GtkAction *undo);
-GtkAction * e_focus_tracker_get_redo_action (EFocusTracker *focus_tracker);
+ EUIAction *undo);
+EUIAction * e_focus_tracker_get_redo_action (EFocusTracker *focus_tracker);
void e_focus_tracker_set_redo_action (EFocusTracker *focus_tracker,
- GtkAction *redo);
+ EUIAction *redo);
void e_focus_tracker_update_actions (EFocusTracker *focus_tracker);
void e_focus_tracker_cut_clipboard (EFocusTracker *focus_tracker);
void e_focus_tracker_copy_clipboard (EFocusTracker *focus_tracker);
diff --git a/src/e-util/e-headerbar-button.c b/src/e-util/e-headerbar-button.c
index 433cd351bb..a235971114 100644
--- a/src/e-util/e-headerbar-button.c
+++ b/src/e-util/e-headerbar-button.c
@@ -14,17 +14,21 @@
#include "evolution-config.h"
-#include "e-headerbar-button.h"
-#include "e-misc-utils.h"
-
#include
+#include "e-misc-utils.h"
+#include "e-ui-action.h"
+#include "e-ui-manager.h"
+
+#include "e-headerbar-button.h"
+
struct _EHeaderBarButtonPrivate {
GtkWidget *labeled_button;
GtkWidget *icon_only_button;
GtkWidget *dropdown_button;
- GtkAction *action;
+ EUIManager *ui_manager;
+ EUIAction *action;
gchar *label;
gchar *prefer_item;
gint last_labeled_button_width;
@@ -35,86 +39,52 @@ enum {
PROP_0,
PROP_PREFER_ITEM,
PROP_LABEL,
- PROP_ACTION
+ PROP_ACTION,
+ PROP_UI_MANAGER
};
G_DEFINE_TYPE_WITH_CODE (EHeaderBarButton, e_header_bar_button, GTK_TYPE_BOX,
G_ADD_PRIVATE (EHeaderBarButton))
-static GtkAction *
+static EUIAction *
header_bar_button_get_prefer_action (EHeaderBarButton *header_bar_button)
{
- GtkMenu *menu;
- GList *children;
- GtkAction *action = NULL;
- GList *link;
- const gchar *prefer_item;
-
- if (header_bar_button->priv->dropdown_button == NULL)
+ if (!header_bar_button->priv->ui_manager ||
+ !header_bar_button->priv->prefer_item)
return NULL;
- menu = gtk_menu_button_get_popup ( GTK_MENU_BUTTON (header_bar_button->priv->dropdown_button));
- g_return_val_if_fail (menu != NULL, NULL);
-
- children = gtk_container_get_children (GTK_CONTAINER (menu));
- g_return_val_if_fail (children != NULL, NULL);
-
- prefer_item = header_bar_button->priv->prefer_item;
-
- for (link = children; link != NULL; link = g_list_next (link)) {
- GtkWidget *child;
- const gchar *name;
-
- child = GTK_WIDGET (link->data);
-
- if (!GTK_IS_MENU_ITEM (child))
- continue;
-
- action = gtk_activatable_get_related_action (
- GTK_ACTIVATABLE (child));
-
- if (action == NULL)
- continue;
-
- name = gtk_action_get_name (action);
-
- if (prefer_item == NULL ||
- *prefer_item == '\0' ||
- g_strcmp0 (name, prefer_item) == 0) {
- break;
- }
- }
-
- g_list_free (children);
-
- return action;
+ return e_ui_manager_get_action (header_bar_button->priv->ui_manager, header_bar_button->priv->prefer_item);
}
static void
-header_bar_button_update_button_for_action (GtkButton *button,
- GtkAction *action)
+header_bar_button_update_button_for_action (EHeaderBarButton *self,
+ GtkButton *button,
+ EUIAction *action,
+ EUIManager *ui_manager)
{
- GtkWidget *image;
GtkStyleContext *style_context;
- const gchar *tooltip;
- const gchar *icon_name;
+ const gchar *value;
g_return_if_fail (button != NULL);
g_return_if_fail (action != NULL);
- tooltip = gtk_action_get_tooltip (action);
- gtk_widget_set_tooltip_text (GTK_WIDGET (button), tooltip);
+ if (ui_manager) {
+ const gchar *const_label = gtk_button_get_label (button);
+ gchar *revert_label = const_label ? e_str_without_underscores (const_label) : NULL;
+ gboolean revert_visible = gtk_widget_get_visible (GTK_WIDGET (button));
- icon_name = gtk_action_get_icon_name (action);
- if (icon_name == NULL) {
- GIcon *icon = gtk_action_get_gicon (action);
- image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_BUTTON);
+ e_ui_manager_update_item_from_action (ui_manager, button, action);
+
+ /* The e_ui_manager_update_item_from_action() sets also the label from the action,
+ but the header bar buttons can have a different label and there can be buttons
+ with icons only too. */
+ gtk_widget_set_visible (GTK_WIDGET (button), revert_visible);
+ gtk_button_set_label (button, revert_label);
+ g_free (revert_label);
} else {
- image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_BUTTON);
+ value = e_ui_action_get_tooltip (action);
+ gtk_widget_set_tooltip_text (GTK_WIDGET (button), value);
}
- gtk_widget_set_margin_end (image, 2);
- gtk_button_set_image (GTK_BUTTON (button), image);
- gtk_widget_show (image);
/* Force text button class to fix some themes */
style_context = gtk_widget_get_style_context (GTK_WIDGET (button));
@@ -124,7 +94,7 @@ header_bar_button_update_button_for_action (GtkButton *button,
static void
header_bar_button_update_button (EHeaderBarButton *header_bar_button)
{
- GtkAction *action;
+ EUIAction *action;
if (header_bar_button->priv->action == NULL)
action = header_bar_button_get_prefer_action (header_bar_button);
@@ -132,42 +102,17 @@ header_bar_button_update_button (EHeaderBarButton *header_bar_button)
action = header_bar_button->priv->action;
if (action != NULL) {
- header_bar_button_update_button_for_action (
+ header_bar_button_update_button_for_action (header_bar_button,
GTK_BUTTON (header_bar_button->priv->labeled_button),
- action);
+ action, header_bar_button->priv->ui_manager);
if (header_bar_button->priv->icon_only_button) {
- header_bar_button_update_button_for_action (
+ header_bar_button_update_button_for_action (NULL,
GTK_BUTTON (header_bar_button->priv->icon_only_button),
- action);
+ action, header_bar_button->priv->ui_manager);
}
}
}
-static void
-header_bar_button_clicked (GtkWidget *button,
- gpointer user_data)
-{
- EHeaderBarButton *header_bar_button = user_data;
- GtkAction *action;
-
- if (header_bar_button->priv->action == NULL)
- action = header_bar_button_get_prefer_action (header_bar_button);
- else
- action = header_bar_button->priv->action;
-
- if (action != NULL)
- gtk_action_activate (action);
-}
-
-static void
-header_bar_button_action_activate_cb (GObject *button,
- gpointer user_data)
-{
- GtkAction *action = user_data;
-
- gtk_action_activate (action);
-}
-
static void
header_bar_button_set_prefer_item (EHeaderBarButton *self,
const gchar *prefer_item)
@@ -178,119 +123,39 @@ header_bar_button_set_prefer_item (EHeaderBarButton *self,
return;
g_free (self->priv->prefer_item);
-
self->priv->prefer_item = g_strdup (prefer_item);
+
header_bar_button_update_button (self);
}
-static gboolean
-header_bar_button_transform_sensitive_cb (GBinding *binding,
- const GValue *from_value,
- GValue *to_value,
- gpointer user_data)
-{
- /* The GtkAction::sensitive property does not take into the consideration
- also the group's sensitivity, thus use the gtk_action_is_sensitive() function. */
-
- g_value_set_boolean (to_value, gtk_action_is_sensitive (GTK_ACTION (g_binding_get_source (binding))));
-
- return TRUE;
-}
-
-typedef struct _ToggleActionData {
- GWeakRef *button_weakref;
- gulong handler_id;
-} ToggleActionData;
-
-static void
-toggle_action_data_free (gpointer ptr,
- GClosure *closure)
-{
- ToggleActionData *tad = ptr;
-
- if (tad) {
- e_weak_ref_free (tad->button_weakref);
- g_free (tad);
- }
-}
-
-static void
-header_button_action_notify_active_cb (GObject *action,
- GParamSpec *param,
- gpointer user_data)
-{
- ToggleActionData *tad = user_data;
- GtkToggleButton *button;
- gboolean active = FALSE;
-
- button = g_weak_ref_get (tad->button_weakref);
- if (!button)
- return;
-
- g_object_get (action, "active", &active, NULL);
-
- /* The "clicked" callback calls gtk_action_activate(), which, in case
- of the toggle action, means to flip the option, thus it calls a notification
- about action's 'active' property change, which leads back here, causing
- a busy loop though the signal handlers. Blocking the handler breaks the loop. */
- if (tad->handler_id)
- g_signal_handler_block (button, tad->handler_id);
-
- if ((gtk_toggle_button_get_active (button) ? 1 : 0) != (active ? 1 : 0))
- gtk_toggle_button_set_active (button, active);
-
- if (tad->handler_id)
- g_signal_handler_unblock (button, tad->handler_id);
-
- g_clear_object (&button);
-}
-
static GtkWidget *
header_bar_button_add_action_button (EHeaderBarButton *header_bar_button,
const gchar *label,
- GtkAction *action,
- GCallback clicked_cb,
- gpointer clicked_cb_user_data)
+ EUIAction *action)
{
GtkWidget *button;
- gulong clicked_handler = 0;
- if (GTK_IS_TOGGLE_ACTION (action))
- button = gtk_toggle_button_new_with_label (label);
- else
+ if (!action) {
button = gtk_button_new_with_label (label);
+ } else if (e_ui_action_get_radio_group (action)) {
+ button = gtk_toggle_button_new_with_label (label);
+ } else {
+ GVariant *state;
+
+ state = g_action_get_state (G_ACTION (action));
+
+ if (state && g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
+ button = gtk_toggle_button_new_with_label (label);
+ else
+ button = gtk_button_new_with_label (label);
+
+ g_clear_pointer (&state, g_variant_unref);
+ }
gtk_box_pack_start (GTK_BOX (header_bar_button), button, FALSE, FALSE, 0);
- if (clicked_cb) {
- clicked_handler = g_signal_connect_object (
- button, "clicked", clicked_cb, clicked_cb_user_data, 0);
- }
-
- if (action) {
- e_binding_bind_property_full (
- action, "sensitive",
- button, "sensitive",
- G_BINDING_SYNC_CREATE,
- header_bar_button_transform_sensitive_cb,
- NULL, NULL, NULL);
-
- if (GTK_IS_TOGGLE_ACTION (action)) {
- ToggleActionData *tad;
-
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
- gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
-
- tad = g_new0 (ToggleActionData, 1);
- tad->button_weakref = e_weak_ref_new (button);
- tad->handler_id = clicked_handler;
-
- g_signal_connect_data (action, "notify::active",
- G_CALLBACK (header_button_action_notify_active_cb), tad, toggle_action_data_free, 0);
- }
-
- header_bar_button_update_button_for_action (GTK_BUTTON (button), action);
- }
+ if (action)
+ header_bar_button_update_button_for_action (header_bar_button, GTK_BUTTON (button), action, header_bar_button->priv->ui_manager);
return button;
}
@@ -298,20 +163,19 @@ header_bar_button_add_action_button (EHeaderBarButton *header_bar_button,
static void
header_bar_button_add_action (EHeaderBarButton *header_bar_button,
const gchar *label,
- GtkAction *action,
- GCallback clicked_cb,
- gpointer clicked_cb_user_data,
+ EUIAction *action,
GtkWidget **out_labeled_button,
GtkWidget **out_icon_only_button)
{
GtkWidget *labeled_button;
GtkWidget *icon_only_button;
- labeled_button = header_bar_button_add_action_button (header_bar_button, label, action, clicked_cb, clicked_cb_user_data);
+ labeled_button = header_bar_button_add_action_button (header_bar_button, label, action);
if (label) {
- icon_only_button = header_bar_button_add_action_button (header_bar_button, NULL, action, clicked_cb, clicked_cb_user_data);
+ icon_only_button = header_bar_button_add_action_button (header_bar_button, NULL, action);
gtk_widget_show (icon_only_button);
+ gtk_widget_hide (labeled_button);
e_binding_bind_property (
labeled_button, "sensitive",
@@ -341,6 +205,24 @@ header_bar_button_style_updated (GtkWidget *widget)
self->priv->last_icon_only_button_width = -1;
}
+static void
+header_bar_button_show_all (GtkWidget *widget)
+{
+ /* visibility of the labeled/only-icon buttons already set, thus do not change them both */
+}
+
+static void
+header_bar_button_unmap (GtkWidget *widget)
+{
+ EHeaderBarButton *self = E_HEADER_BAR_BUTTON (widget);
+
+ /* Chain up to parent's method. */
+ GTK_WIDGET_CLASS (e_header_bar_button_parent_class)->unmap (widget);
+
+ self->priv->last_labeled_button_width = -1;
+ self->priv->last_icon_only_button_width = -1;
+}
+
static void
header_bar_button_set_property (GObject *object,
guint property_id,
@@ -360,9 +242,12 @@ header_bar_button_set_property (GObject *object,
header_bar_button->priv->label = g_value_dup_string (value);
return;
case PROP_ACTION:
- header_bar_button->priv->action = g_value_get_object (value);
- if (header_bar_button->priv->action != NULL)
- g_object_ref (header_bar_button->priv->action);
+ g_clear_object (&header_bar_button->priv->action);
+ header_bar_button->priv->action = g_value_dup_object (value);
+ return;
+ case PROP_UI_MANAGER:
+ g_clear_object (&header_bar_button->priv->ui_manager);
+ header_bar_button->priv->ui_manager = g_value_dup_object (value);
return;
}
@@ -388,6 +273,9 @@ header_bar_button_get_property (GObject *object,
case PROP_ACTION:
g_value_set_object (value, header_bar_button->priv->action);
return;
+ case PROP_UI_MANAGER:
+ g_value_set_object (value, header_bar_button->priv->ui_manager);
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -405,8 +293,6 @@ header_bar_button_constructed (GObject *object)
header_bar_button_add_action (header_bar_button,
header_bar_button->priv->label,
header_bar_button->priv->action,
- G_CALLBACK (header_bar_button_clicked),
- header_bar_button,
&header_bar_button->priv->labeled_button,
&header_bar_button->priv->icon_only_button);
@@ -424,8 +310,8 @@ header_bar_button_finalize (GObject *object)
g_free (header_bar_button->priv->prefer_item);
g_free (header_bar_button->priv->label);
- if (header_bar_button->priv->action != NULL)
- g_object_unref (header_bar_button->priv->action);
+ g_clear_object (&header_bar_button->priv->action);
+ g_clear_object (&header_bar_button->priv->ui_manager);
/* Chain up to parent's method. */
G_OBJECT_CLASS (e_header_bar_button_parent_class)->finalize (object);
@@ -445,6 +331,8 @@ e_header_bar_button_class_init (EHeaderBarButtonClass *class)
widget_class = GTK_WIDGET_CLASS (class);
widget_class->style_updated = header_bar_button_style_updated;
+ widget_class->show_all = header_bar_button_show_all;
+ widget_class->unmap = header_bar_button_unmap;
g_object_class_install_property (
object_class,
@@ -473,7 +361,17 @@ e_header_bar_button_class_init (EHeaderBarButtonClass *class)
"action",
"Action",
"Button action",
- GTK_TYPE_ACTION,
+ E_TYPE_UI_ACTION,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_UI_MANAGER,
+ g_param_spec_object (
+ "ui-manager",
+ "EUIManager",
+ NULL,
+ E_TYPE_UI_MANAGER,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
@@ -491,7 +389,9 @@ e_header_bar_button_init (EHeaderBarButton *self)
/**
* e_header_bar_button_new:
- * @action: An action overriding menu default action
+ * @label: a button label
+ * @action: an #EUIAction overriding menu default action
+ * @ui_manager: an #EUIManager to get an icon for the @action
*
* Creates a new #EHeaderBarButton labeled with @label.
* If action is %NULL, button will use default preferred menu action if available,
@@ -499,15 +399,18 @@ e_header_bar_button_init (EHeaderBarButton *self)
*
* Returns: (transfer full): a new #EHeaderBarButton
*
- * Since: 3.46
+ * Since: 3.56
**/
-GtkWidget*
+GtkWidget *
e_header_bar_button_new (const gchar *label,
- GtkAction *action)
+ EUIAction *action,
+ EUIManager *ui_manager)
{
return g_object_new (E_TYPE_HEADER_BAR_BUTTON,
"label", label,
- "action", action, NULL);
+ "action", action,
+ "ui-manager", ui_manager,
+ NULL);
}
/**
@@ -518,18 +421,18 @@ e_header_bar_button_new (const gchar *label,
*
* Adds a new button with a related action.
*
- * Since: 3.46
+ * Since: 3.56
**/
void
e_header_bar_button_add_action (EHeaderBarButton *header_bar_button,
const gchar *label,
- GtkAction *action)
+ EUIAction *action)
{
g_return_if_fail (E_IS_HEADER_BAR_BUTTON (header_bar_button));
- g_return_if_fail (GTK_IS_ACTION (action));
+ g_return_if_fail (E_IS_UI_ACTION (action));
header_bar_button_add_action (header_bar_button, label, action,
- G_CALLBACK (header_bar_button_action_activate_cb), action, NULL, NULL);
+ NULL, NULL);
}
/**
@@ -623,7 +526,7 @@ e_header_bar_button_css_add_class (EHeaderBarButton *header_bar_button,
**/
void
e_header_bar_button_add_accelerator (EHeaderBarButton *header_bar_button,
- GtkAccelGroup* accel_group,
+ GtkAccelGroup *accel_group,
guint accel_key,
GdkModifierType accel_mods,
GtkAccelFlags accel_flags)
diff --git a/src/e-util/e-headerbar-button.h b/src/e-util/e-headerbar-button.h
index 87a998e9a2..5109104d28 100644
--- a/src/e-util/e-headerbar-button.h
+++ b/src/e-util/e-headerbar-button.h
@@ -14,6 +14,9 @@
#include
+#include
+#include
+
/* Standard GObject macros */
#define E_TYPE_HEADER_BAR_BUTTON \
(e_header_bar_button_get_type ())
@@ -49,11 +52,12 @@ struct _EHeaderBarButtonClass {
};
GType e_header_bar_button_get_type (void) G_GNUC_CONST;
-GtkWidget* e_header_bar_button_new (const gchar *label,
- GtkAction *action);
+GtkWidget * e_header_bar_button_new (const gchar *label,
+ EUIAction *action,
+ EUIManager *ui_manager);
void e_header_bar_button_add_action (EHeaderBarButton *header_bar_button,
const gchar *label,
- GtkAction *action);
+ EUIAction *action);
void e_header_bar_button_take_menu (EHeaderBarButton *header_bar_button,
GtkWidget *menu);
void e_header_bar_button_css_add_class (EHeaderBarButton *header_bar_button,
diff --git a/src/e-util/e-headerbar.c b/src/e-util/e-headerbar.c
index e9c75e57d6..4dfaf77f5c 100644
--- a/src/e-util/e-headerbar.c
+++ b/src/e-util/e-headerbar.c
@@ -384,6 +384,15 @@ header_bar_realize (GtkWidget *widget)
header_bar_update_buttons (E_HEADER_BAR (widget), -1);
}
+static void
+header_bar_map (GtkWidget *widget)
+{
+ /* Chain up to parent's method. */
+ GTK_WIDGET_CLASS (e_header_bar_parent_class)->map (widget);
+
+ header_bar_update_buttons (E_HEADER_BAR (widget), -1);
+}
+
static void
header_bar_get_preferred_width (GtkWidget *in_widget,
gint *minimum_width,
@@ -449,6 +458,8 @@ header_bar_constructed (GObject *object)
self->priv->end_buttons = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, BUTTON_SPACING);
gtk_header_bar_pack_end (GTK_HEADER_BAR (self), self->priv->end_buttons);
gtk_widget_show (self->priv->end_buttons);
+
+ gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (self)), "titlebar");
}
static void
@@ -481,6 +492,7 @@ e_header_bar_class_init (EHeaderBarClass *klass)
widget_class = GTK_WIDGET_CLASS (klass);
widget_class->size_allocate = header_bar_size_allocate;
widget_class->realize = header_bar_realize;
+ widget_class->map = header_bar_map;
widget_class->get_preferred_width = header_bar_get_preferred_width;
object_class = G_OBJECT_CLASS (klass);
@@ -566,6 +578,38 @@ e_header_bar_pack_end (EHeaderBar *self,
header_bar_set_label_priority (self, widget, label_priority);
}
+/**
+ * e_header_bar_remove_all:
+ * @self: an #EHeaderBar
+ *
+ * Removes all children of the @self added by e_header_bar_pack_start()
+ * and e_header_bar_pack_end().
+ *
+ * Since: 3.56
+ **/
+void
+e_header_bar_remove_all (EHeaderBar *self)
+{
+ GtkContainer *container;
+ GList *children, *link;
+
+ g_return_if_fail (E_IS_HEADER_BAR (self));
+
+ container = GTK_CONTAINER (self->priv->start_buttons);
+ children = gtk_container_get_children (container);
+ for (link = children; link; link = g_list_next (link)) {
+ gtk_container_remove (container, link->data);
+ }
+ g_list_free (children);
+
+ container = GTK_CONTAINER (self->priv->end_buttons);
+ children = gtk_container_get_children (container);
+ for (link = children; link; link = g_list_next (link)) {
+ gtk_container_remove (container, link->data);
+ }
+ g_list_free (children);
+}
+
/**
* e_header_bar_get_start_widgets:
* @self: an #EHeaderBar
diff --git a/src/e-util/e-headerbar.h b/src/e-util/e-headerbar.h
index 92b6b21959..d6bd8f2c72 100644
--- a/src/e-util/e-headerbar.h
+++ b/src/e-util/e-headerbar.h
@@ -54,6 +54,7 @@ void e_header_bar_pack_start (EHeaderBar *self,
void e_header_bar_pack_end (EHeaderBar *self,
GtkWidget *widget,
guint label_priority);
+void e_header_bar_remove_all (EHeaderBar *self);
GList * e_header_bar_get_start_widgets (EHeaderBar *self);
GList * e_header_bar_get_end_widgets (EHeaderBar *self);
diff --git a/src/e-util/e-html-editor-actions.c b/src/e-util/e-html-editor-actions.c
index aa80657afc..4f7e11c248 100644
--- a/src/e-util/e-html-editor-actions.c
+++ b/src/e-util/e-html-editor-actions.c
@@ -27,7 +27,6 @@
#include "e-html-editor.h"
#include "e-html-editor-private.h"
#include "e-html-editor-actions.h"
-#include "e-emoticon-action.h"
#include "e-emoticon-chooser.h"
#include "e-gtkemojichooser.h"
#include "e-image-chooser-dialog.h"
@@ -141,9 +140,11 @@ insert_text_file_ready_cb (GFile *file,
*****************************************************************************/
static void
-action_copy_link_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_copy_link_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
GtkClipboard *clipboard;
if (!editor->priv->context_hover_uri)
@@ -159,9 +160,11 @@ action_copy_link_cb (GtkAction *action,
}
static void
-action_open_link_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_open_link_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
gpointer parent;
if (!editor->priv->context_hover_uri)
@@ -174,9 +177,11 @@ action_open_link_cb (GtkAction *action,
}
static void
-action_context_delete_cell_contents_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_context_delete_cell_contents_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
cnt_editor = e_html_editor_get_content_editor (editor);
@@ -184,9 +189,11 @@ action_context_delete_cell_contents_cb (GtkAction *action,
}
static void
-action_context_delete_column_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_context_delete_column_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
cnt_editor = e_html_editor_get_content_editor (editor);
@@ -194,9 +201,11 @@ action_context_delete_column_cb (GtkAction *action,
}
static void
-action_context_delete_row_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_context_delete_row_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
cnt_editor = e_html_editor_get_content_editor (editor);
@@ -204,9 +213,11 @@ action_context_delete_row_cb (GtkAction *action,
}
static void
-action_context_delete_table_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_context_delete_table_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
cnt_editor = e_html_editor_get_content_editor (editor);
@@ -214,9 +225,11 @@ action_context_delete_table_cb (GtkAction *action,
}
static void
-action_context_delete_hrule_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_context_delete_hrule_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
cnt_editor = e_html_editor_get_content_editor (editor);
@@ -224,9 +237,11 @@ action_context_delete_hrule_cb (GtkAction *action,
}
static void
-action_context_delete_image_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_context_delete_image_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
cnt_editor = e_html_editor_get_content_editor (editor);
@@ -234,9 +249,11 @@ action_context_delete_image_cb (GtkAction *action,
}
static void
-action_context_insert_column_after_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_context_insert_column_after_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
cnt_editor = e_html_editor_get_content_editor (editor);
@@ -244,9 +261,11 @@ action_context_insert_column_after_cb (GtkAction *action,
}
static void
-action_context_insert_column_before_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_context_insert_column_before_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
cnt_editor = e_html_editor_get_content_editor (editor);
@@ -254,9 +273,11 @@ action_context_insert_column_before_cb (GtkAction *action,
}
static void
-action_context_insert_row_above_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_context_insert_row_above_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
cnt_editor = e_html_editor_get_content_editor (editor);
@@ -264,9 +285,11 @@ action_context_insert_row_above_cb (GtkAction *action,
}
static void
-action_context_insert_row_below_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_context_insert_row_below_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
cnt_editor = e_html_editor_get_content_editor (editor);
@@ -274,9 +297,11 @@ action_context_insert_row_below_cb (GtkAction *action,
}
static void
-action_context_remove_link_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_context_remove_link_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
cnt_editor = e_html_editor_get_content_editor (editor);
@@ -284,9 +309,11 @@ action_context_remove_link_cb (GtkAction *action,
}
static void
-action_context_spell_add_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_context_spell_add_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
ESpellChecker *spell_checker;
gchar *word;
@@ -301,9 +328,46 @@ action_context_spell_add_cb (GtkAction *action,
}
static void
-action_context_spell_ignore_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_context_spell_add_to_dict_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+ EContentEditor *cnt_editor;
+ ESpellChecker *spell_checker;
+ ESpellDictionary *dictionary;
+ GVariant *state;
+
+ state = g_action_get_state (G_ACTION (action));
+ g_warn_if_fail (state != NULL);
+
+ if (!state)
+ return;
+
+ cnt_editor = e_html_editor_get_content_editor (editor);
+ spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
+ dictionary = e_spell_checker_ref_dictionary (spell_checker, g_variant_get_string (state, NULL));
+
+ if (dictionary) {
+ gchar *word;
+
+ word = e_content_editor_get_caret_word (cnt_editor);
+ if (word && *word)
+ e_spell_dictionary_learn_word (dictionary, word, -1);
+ g_free (word);
+ }
+
+ g_clear_object (&dictionary);
+ g_clear_object (&spell_checker);
+ g_variant_unref (state);
+}
+
+static void
+action_context_spell_ignore_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
+{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
ESpellChecker *spell_checker;
gchar *word;
@@ -318,9 +382,11 @@ action_context_spell_ignore_cb (GtkAction *action,
}
static void
-action_indent_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_indent_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
cnt_editor = e_html_editor_get_content_editor (editor);
@@ -344,9 +410,11 @@ emoji_chooser_emoji_picked_cb (EHTMLEditor *editor,
}
static void
-action_insert_emoji_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_insert_emoji_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
GtkPopover *popover;
GtkWidget *relative_to;
@@ -387,13 +455,20 @@ action_insert_emoji_cb (GtkAction *action,
}
static void
-action_insert_emoticon_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_insert_emoticon_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
- EEmoticon *emoticon;
+ const EEmoticon *emoticon;
+ const gchar *icon_name;
+
+ g_return_if_fail (parameter != NULL);
+
+ icon_name = g_variant_get_string (parameter, NULL);
+ emoticon = e_emoticon_chooser_lookup_emoticon (icon_name);
- emoticon = e_emoticon_chooser_get_current_emoticon (E_EMOTICON_CHOOSER (action));
g_return_if_fail (emoticon != NULL);
cnt_editor = e_html_editor_get_content_editor (editor);
@@ -401,9 +476,11 @@ action_insert_emoticon_cb (GtkAction *action,
}
static void
-action_insert_html_file_cb (GtkToggleAction *action,
- EHTMLEditor *editor)
+action_insert_html_file_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
GtkFileChooserNative *native;
GtkFileFilter *filter;
GtkWidget *toplevel;
@@ -441,9 +518,11 @@ action_insert_html_file_cb (GtkToggleAction *action,
}
static void
-action_insert_image_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_insert_image_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
GtkWidget *dialog = NULL;
GtkFileChooserNative *native = NULL;
GtkWidget *toplevel;
@@ -515,9 +594,12 @@ action_insert_image_cb (GtkAction *action,
}
static void
-action_insert_link_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_insert_link_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
if (!editor->priv->link_popover)
editor->priv->link_popover = e_html_editor_link_popover_new (editor);
@@ -525,9 +607,12 @@ action_insert_link_cb (GtkAction *action,
}
static void
-action_insert_rule_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_insert_rule_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
if (editor->priv->hrule_dialog == NULL)
editor->priv->hrule_dialog =
e_html_editor_hrule_dialog_new (editor);
@@ -536,9 +621,12 @@ action_insert_rule_cb (GtkAction *action,
}
static void
-action_insert_table_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_insert_table_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
if (editor->priv->table_dialog == NULL)
editor->priv->table_dialog =
e_html_editor_table_dialog_new (editor);
@@ -547,9 +635,11 @@ action_insert_table_cb (GtkAction *action,
}
static void
-action_insert_text_file_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_insert_text_file_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
GtkFileChooserNative *native;
GtkWidget *toplevel;
GtkFileFilter *filter;
@@ -591,29 +681,32 @@ editor_actions_add_to_recent_languages (EHTMLEditor *editor,
const gchar *language_code);
static void
-action_language_cb (GtkToggleAction *toggle_action,
- EHTMLEditor *editor)
+action_language_notify_active_cb (EUIAction *action,
+ GParamSpec *param,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
ESpellChecker *spell_checker;
EContentEditor *cnt_editor;
const gchar *language_code;
- GtkAction *add_action;
- gchar *action_name;
+ EUIAction *add_action;
+ gchar action_name[128];
gboolean active;
+ e_ui_menu_freeze (editor->priv->main_menu);
+
cnt_editor = e_html_editor_get_content_editor (editor);
spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
- language_code = gtk_action_get_name (GTK_ACTION (toggle_action));
+ language_code = g_action_get_name (G_ACTION (action));
- active = gtk_toggle_action_get_active (toggle_action);
+ active = e_ui_action_get_active (action);
e_spell_checker_set_language_active (spell_checker, language_code, active);
g_clear_object (&spell_checker);
/* Update "Add Word To" context menu item visibility. */
- action_name = g_strdup_printf ("context-spell-add-%s", language_code);
+ g_warn_if_fail (g_snprintf (action_name, sizeof (action_name), "context-spell-add-%s", language_code) < sizeof (action_name));
add_action = e_html_editor_get_action (editor, action_name);
- gtk_action_set_visible (add_action, active);
- g_free (action_name);
+ e_ui_action_set_visible (add_action, active);
e_html_editor_update_spell_actions (editor);
@@ -625,7 +718,7 @@ action_language_cb (GtkToggleAction *toggle_action,
gchar **strv;
gint ii, max_items;
- gtk_ui_manager_remove_ui (editor->priv->manager, editor->priv->recent_spell_languages_merge_id);
+ g_menu_remove_all (editor->priv->recent_languages_menu);
settings = e_util_ref_settings ("org.gnome.evolution.mail");
strv = g_settings_get_strv (settings, "composer-spell-languages-recently-used");
@@ -653,6 +746,8 @@ action_language_cb (GtkToggleAction *toggle_action,
g_ptr_array_free (array, TRUE);
g_strfreev (strv);
}
+
+ e_ui_menu_thaw (editor->priv->main_menu);
}
static gboolean
@@ -661,7 +756,7 @@ update_mode_combobox (gpointer data)
GWeakRef *weak_ref = data;
EHTMLEditor *editor;
EContentEditorMode mode;
- GtkAction *action;
+ EUIAction *action;
editor = g_weak_ref_get (weak_ref);
if (!editor)
@@ -669,8 +764,8 @@ update_mode_combobox (gpointer data)
mode = e_html_editor_get_mode (editor);
- action = gtk_action_group_get_action (editor->priv->core_editor_actions, "mode-html");
- gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), mode);
+ action = e_ui_action_group_get_action (editor->priv->core_editor_actions, "mode-html");
+ e_ui_action_set_state (action, g_variant_new_int32 (mode));
g_object_unref (editor);
@@ -682,28 +777,22 @@ html_editor_actions_notify_mode_cb (EHTMLEditor *editor,
GParamSpec *param,
gpointer user_data)
{
- GtkActionGroup *action_group;
- GtkWidget *style_combo_box;
gboolean is_html;
g_return_if_fail (E_IS_HTML_EDITOR (editor));
is_html = e_html_editor_get_mode (editor) == E_CONTENT_EDITOR_MODE_HTML;
+ e_ui_manager_freeze (editor->priv->ui_manager);
+
g_object_set (G_OBJECT (editor->priv->html_actions), "sensitive", is_html, NULL);
/* This must be done from idle callback, because apparently we can change
* current value in callback of current value change */
g_idle_add_full (G_PRIORITY_HIGH_IDLE, update_mode_combobox, e_weak_ref_new (editor), (GDestroyNotify) e_weak_ref_free);
- action_group = editor->priv->html_actions;
- gtk_action_group_set_visible (action_group, is_html);
-
- action_group = editor->priv->html_context_actions;
- gtk_action_group_set_visible (action_group, is_html);
-
- gtk_widget_set_sensitive (editor->priv->fg_color_combo_box, is_html);
- gtk_widget_set_sensitive (editor->priv->bg_color_combo_box, is_html);
+ e_ui_action_group_set_visible (editor->priv->html_actions, is_html);
+ e_ui_action_group_set_visible (editor->priv->html_context_actions, is_html);
if (is_html && gtk_widget_get_visible (editor->priv->edit_toolbar)) {
gtk_widget_show (editor->priv->html_toolbar);
@@ -712,33 +801,24 @@ html_editor_actions_notify_mode_cb (EHTMLEditor *editor,
}
/* Certain paragraph styles are HTML-only. */
- gtk_action_set_sensitive (ACTION (STYLE_H1), is_html);
- gtk_action_set_visible (ACTION (STYLE_H1), is_html);
- gtk_action_set_sensitive (ACTION (STYLE_H2), is_html);
- gtk_action_set_visible (ACTION (STYLE_H2), is_html);
- gtk_action_set_sensitive (ACTION (STYLE_H3), is_html);
- gtk_action_set_visible (ACTION (STYLE_H3), is_html);
- gtk_action_set_sensitive (ACTION (STYLE_H4), is_html);
- gtk_action_set_visible (ACTION (STYLE_H4), is_html);
- gtk_action_set_sensitive (ACTION (STYLE_H5), is_html);
- gtk_action_set_visible (ACTION (STYLE_H5), is_html);
- gtk_action_set_sensitive (ACTION (STYLE_H6), is_html);
- gtk_action_set_visible (ACTION (STYLE_H6), is_html);
- gtk_action_set_sensitive (ACTION (STYLE_ADDRESS), is_html);
- gtk_action_set_visible (ACTION (STYLE_ADDRESS), is_html);
+ e_ui_action_set_sensitive (ACTION (STYLE_H1), is_html);
+ e_ui_action_set_visible (ACTION (STYLE_H1), is_html);
+ e_ui_action_set_sensitive (ACTION (STYLE_H2), is_html);
+ e_ui_action_set_visible (ACTION (STYLE_H2), is_html);
+ e_ui_action_set_sensitive (ACTION (STYLE_H3), is_html);
+ e_ui_action_set_visible (ACTION (STYLE_H3), is_html);
+ e_ui_action_set_sensitive (ACTION (STYLE_H4), is_html);
+ e_ui_action_set_visible (ACTION (STYLE_H4), is_html);
+ e_ui_action_set_sensitive (ACTION (STYLE_H5), is_html);
+ e_ui_action_set_visible (ACTION (STYLE_H5), is_html);
+ e_ui_action_set_sensitive (ACTION (STYLE_H6), is_html);
+ e_ui_action_set_visible (ACTION (STYLE_H6), is_html);
+ e_ui_action_set_sensitive (ACTION (STYLE_ADDRESS), is_html);
+ e_ui_action_set_visible (ACTION (STYLE_ADDRESS), is_html);
- /* Hide them from the action combo box as well */
- style_combo_box = e_html_editor_get_style_combo_box (editor);
- e_action_combo_box_update_model (E_ACTION_COMBO_BOX (style_combo_box));
-}
+ e_html_editor_emit_after_mode_changed (editor);
-static void
-action_mode_cb (GtkRadioAction *action,
- GtkRadioAction *current,
- EHTMLEditor *editor)
-{
- /* Nothing to do here, wait for notification of
- a property change from the EContentEditor */
+ e_ui_manager_thaw (editor->priv->ui_manager);
}
static void
@@ -761,9 +841,11 @@ clipboard_text_received_for_paste_as_text (GtkClipboard *clipboard,
}
static void
-action_paste_as_text_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_paste_as_text_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
GtkClipboard *clipboard;
@@ -827,9 +909,11 @@ clipboard_text_received_for_paste_quote (GtkClipboard *clipboard,
}
static void
-action_paste_quote_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_paste_quote_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
GtkClipboard *clipboard;
@@ -855,9 +939,12 @@ action_paste_quote_cb (GtkAction *action,
}
static void
-action_properties_cell_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_properties_cell_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
if (editor->priv->cell_dialog == NULL) {
editor->priv->cell_dialog =
e_html_editor_cell_dialog_new (editor);
@@ -867,9 +954,12 @@ action_properties_cell_cb (GtkAction *action,
}
static void
-action_properties_image_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_properties_image_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
if (editor->priv->image_dialog == NULL) {
editor->priv->image_dialog =
e_html_editor_image_dialog_new (editor);
@@ -880,9 +970,12 @@ action_properties_image_cb (GtkAction *action,
}
static void
-action_properties_link_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_properties_link_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
if (!editor->priv->link_popover)
editor->priv->link_popover = e_html_editor_link_popover_new (editor);
@@ -890,9 +983,12 @@ action_properties_link_cb (GtkAction *action,
}
static void
-action_properties_page_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_properties_page_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
if (editor->priv->page_dialog == NULL) {
editor->priv->page_dialog =
e_html_editor_page_dialog_new (editor);
@@ -902,9 +998,12 @@ action_properties_page_cb (GtkAction *action,
}
static void
-action_properties_paragraph_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_properties_paragraph_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
if (editor->priv->paragraph_dialog == NULL) {
editor->priv->paragraph_dialog =
e_html_editor_paragraph_dialog_new (editor);
@@ -914,9 +1013,12 @@ action_properties_paragraph_cb (GtkAction *action,
}
static void
-action_properties_rule_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_properties_rule_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
if (editor->priv->hrule_dialog == NULL) {
editor->priv->hrule_dialog =
e_html_editor_hrule_dialog_new (editor);
@@ -926,9 +1028,12 @@ action_properties_rule_cb (GtkAction *action,
}
static void
-action_properties_table_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_properties_table_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
if (editor->priv->table_dialog == NULL) {
editor->priv->table_dialog =
e_html_editor_table_dialog_new (editor);
@@ -938,9 +1043,12 @@ action_properties_table_cb (GtkAction *action,
}
static void
-action_properties_text_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_properties_text_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
if (editor->priv->text_dialog == NULL) {
editor->priv->text_dialog =
e_html_editor_text_dialog_new (editor);
@@ -950,9 +1058,11 @@ action_properties_text_cb (GtkAction *action,
}
static void
-action_redo_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_redo_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
cnt_editor = e_html_editor_get_content_editor (editor);
@@ -961,21 +1071,27 @@ action_redo_cb (GtkAction *action,
}
static void
-action_show_find_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_show_find_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
if (editor->priv->find_dialog == NULL) {
editor->priv->find_dialog = e_html_editor_find_dialog_new (editor);
- gtk_action_set_sensitive (ACTION (FIND_AGAIN), TRUE);
+ e_ui_action_set_sensitive (ACTION (FIND_AGAIN), TRUE);
}
gtk_window_present (GTK_WINDOW (editor->priv->find_dialog));
}
static void
-action_find_again_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_find_again_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
if (editor->priv->find_dialog == NULL)
return;
@@ -984,9 +1100,12 @@ action_find_again_cb (GtkAction *action,
}
static void
-action_show_replace_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_show_replace_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
if (editor->priv->replace_dialog == NULL) {
editor->priv->replace_dialog =
e_html_editor_replace_dialog_new (editor);
@@ -996,9 +1115,12 @@ action_show_replace_cb (GtkAction *action,
}
static void
-action_spell_check_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_spell_check_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
if (editor->priv->spell_check_dialog == NULL) {
editor->priv->spell_check_dialog =
e_html_editor_spell_check_dialog_new (editor);
@@ -1008,9 +1130,11 @@ action_spell_check_cb (GtkAction *action,
}
static void
-action_undo_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_undo_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
cnt_editor = e_html_editor_get_content_editor (editor);
@@ -1020,9 +1144,11 @@ action_undo_cb (GtkAction *action,
}
static void
-action_unindent_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_unindent_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
cnt_editor = e_html_editor_get_content_editor (editor);
@@ -1031,9 +1157,11 @@ action_unindent_cb (GtkAction *action,
}
static void
-action_wrap_lines_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_wrap_lines_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
cnt_editor = e_html_editor_get_content_editor (editor);
@@ -1044,9 +1172,9 @@ action_wrap_lines_cb (GtkAction *action,
/* This is when the user toggled the action */
static void
manage_format_subsuperscript_toggled (EHTMLEditor *editor,
- GtkToggleAction *changed_action,
+ EUIAction *changed_action,
const gchar *prop_name,
- GtkToggleAction *second_action)
+ EUIAction *second_action)
{
EContentEditor *cnt_editor = e_html_editor_get_content_editor (editor);
@@ -1054,11 +1182,11 @@ manage_format_subsuperscript_toggled (EHTMLEditor *editor,
g_signal_handlers_block_matched (changed_action, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
g_signal_handlers_block_matched (second_action, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
- if (gtk_toggle_action_get_active (changed_action) &&
- gtk_toggle_action_get_active (second_action))
- gtk_toggle_action_set_active (second_action, FALSE);
+ if (e_ui_action_get_active (changed_action) &&
+ e_ui_action_get_active (second_action))
+ e_ui_action_set_active (second_action, FALSE);
- g_object_set (G_OBJECT (cnt_editor), prop_name, gtk_toggle_action_get_active (changed_action), NULL);
+ g_object_set (G_OBJECT (cnt_editor), prop_name, e_ui_action_get_active (changed_action), NULL);
g_signal_handlers_unblock_matched (second_action, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
g_signal_handlers_unblock_matched (changed_action, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
@@ -1068,9 +1196,9 @@ manage_format_subsuperscript_toggled (EHTMLEditor *editor,
/* This is when the content editor claimed change on the property */
static void
manage_format_subsuperscript_notify (EHTMLEditor *editor,
- GtkToggleAction *changed_action,
+ EUIAction *changed_action,
const gchar *prop_name,
- GtkToggleAction *second_action)
+ EUIAction *second_action)
{
EContentEditor *cnt_editor = e_html_editor_get_content_editor (editor);
gboolean value = FALSE;
@@ -1081,11 +1209,11 @@ manage_format_subsuperscript_notify (EHTMLEditor *editor,
g_object_get (G_OBJECT (cnt_editor), prop_name, &value, NULL);
- gtk_toggle_action_set_active (changed_action, value);
+ e_ui_action_set_active (changed_action, value);
- if (gtk_toggle_action_get_active (changed_action) &&
- gtk_toggle_action_get_active (second_action))
- gtk_toggle_action_set_active (second_action, FALSE);
+ if (e_ui_action_get_active (changed_action) &&
+ e_ui_action_get_active (second_action))
+ e_ui_action_set_active (second_action, FALSE);
g_signal_handlers_unblock_matched (second_action, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
g_signal_handlers_unblock_matched (changed_action, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
@@ -1093,12 +1221,15 @@ manage_format_subsuperscript_notify (EHTMLEditor *editor,
}
static void
-html_editor_actions_subscript_toggled_cb (GtkToggleAction *action,
- EHTMLEditor *editor)
+html_editor_actions_subscript_toggled_cb (EUIAction *action,
+ GParamSpec *param,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
g_return_if_fail (E_IS_HTML_EDITOR (editor));
- manage_format_subsuperscript_toggled (editor, GTK_TOGGLE_ACTION (ACTION (SUBSCRIPT)), "subscript", GTK_TOGGLE_ACTION (ACTION (SUPERSCRIPT)));
+ manage_format_subsuperscript_toggled (editor, ACTION (SUBSCRIPT), "subscript", ACTION (SUPERSCRIPT));
}
static void
@@ -1108,16 +1239,19 @@ html_editor_actions_notify_subscript_cb (EContentEditor *cnt_editor,
{
g_return_if_fail (E_IS_HTML_EDITOR (editor));
- manage_format_subsuperscript_notify (editor, GTK_TOGGLE_ACTION (ACTION (SUBSCRIPT)), "subscript", GTK_TOGGLE_ACTION (ACTION (SUPERSCRIPT)));
+ manage_format_subsuperscript_notify (editor, ACTION (SUBSCRIPT), "subscript", ACTION (SUPERSCRIPT));
}
static void
-html_editor_actions_superscript_toggled_cb (GtkToggleAction *action,
- EHTMLEditor *editor)
+html_editor_actions_superscript_toggled_cb (EUIAction *action,
+ GParamSpec *param,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
g_return_if_fail (E_IS_HTML_EDITOR (editor));
- manage_format_subsuperscript_toggled (editor, GTK_TOGGLE_ACTION (ACTION (SUPERSCRIPT)), "superscript", GTK_TOGGLE_ACTION (ACTION (SUBSCRIPT)));
+ manage_format_subsuperscript_toggled (editor, ACTION (SUPERSCRIPT), "superscript", ACTION (SUBSCRIPT));
}
static void
@@ -1127,7 +1261,7 @@ html_editor_actions_notify_superscript_cb (EContentEditor *cnt_editor,
{
g_return_if_fail (E_IS_HTML_EDITOR (editor));
- manage_format_subsuperscript_notify (editor, GTK_TOGGLE_ACTION (ACTION (SUPERSCRIPT)), "superscript", GTK_TOGGLE_ACTION (ACTION (SUBSCRIPT)));
+ manage_format_subsuperscript_notify (editor, ACTION (SUPERSCRIPT), "superscript", ACTION (SUBSCRIPT));
}
@@ -1137,49 +1271,49 @@ html_editor_actions_notify_superscript_cb (EContentEditor *cnt_editor,
* These actions are always enabled.
*****************************************************************************/
-static GtkActionEntry core_entries[] = {
+static const EUIActionEntry core_entries[] = {
{ "copy",
"edit-copy",
N_("_Copy"),
"c",
N_("Copy selected text to the clipboard"),
- NULL }, /* Handled by focus tracker */
+ NULL, NULL, NULL, NULL }, /* Handled by focus tracker */
{ "cut",
"edit-cut",
N_("Cu_t"),
"x",
N_("Cut selected text to the clipboard"),
- NULL }, /* Handled by focus tracker */
+ NULL, NULL, NULL, NULL }, /* Handled by focus tracker */
{ "paste",
"edit-paste",
N_("_Paste"),
"v",
N_("Paste text from the clipboard"),
- NULL }, /* Handled by focus tracker */
+ NULL, NULL, NULL, NULL }, /* Handled by focus tracker */
{ "redo",
"edit-redo",
N_("_Redo"),
"z",
N_("Redo the last undone action"),
- G_CALLBACK (action_redo_cb) },
+ action_redo_cb, NULL, NULL, NULL },
{ "select-all",
"edit-select-all",
N_("Select _All"),
"a",
NULL,
- NULL }, /* Handled by focus tracker */
+ NULL, NULL, NULL, NULL }, /* Handled by focus tracker */
{ "undo",
"edit-undo",
N_("_Undo"),
"z",
N_("Undo the last action"),
- G_CALLBACK (action_undo_cb) },
+ action_undo_cb, NULL, NULL, NULL },
/* Menus */
@@ -1188,306 +1322,371 @@ static GtkActionEntry core_entries[] = {
N_("_Edit"),
NULL,
NULL,
- NULL },
+ NULL, NULL, NULL, NULL },
{ "file-menu",
NULL,
N_("_File"),
NULL,
NULL,
- NULL },
+ NULL, NULL, NULL, NULL },
{ "format-menu",
NULL,
N_("For_mat"),
NULL,
NULL,
- NULL },
+ NULL, NULL, NULL, NULL },
{ "paragraph-style-menu",
NULL,
N_("_Paragraph Style"),
NULL,
NULL,
- NULL },
+ NULL, NULL, NULL, NULL },
{ "insert-menu",
NULL,
N_("_Insert"),
NULL,
NULL,
- NULL },
+ NULL, NULL, NULL, NULL },
{ "justify-menu",
NULL,
N_("_Alignment"),
NULL,
NULL,
- NULL },
+ NULL, NULL, NULL, NULL },
{ "language-menu",
NULL,
N_("Current _Languages"),
NULL,
NULL,
- NULL },
+ NULL, NULL, NULL, NULL },
{ "view-menu",
NULL,
N_("_View"),
NULL,
NULL,
- NULL }
+ NULL, NULL, NULL, NULL },
+
+ /* fake actions, related to dynamic items */
+
+ { "EHTMLEditor::recent-languages",
+ NULL,
+ N_("Recent spell check languages"),
+ NULL,
+ NULL,
+ NULL, NULL, NULL, NULL },
+
+ { "EHTMLEditor::all-languages",
+ NULL,
+ N_("All spell check languages"),
+ NULL,
+ NULL,
+ NULL, NULL, NULL, NULL },
+
+ { "EHTMLEditor::editing-mode",
+ NULL,
+ N_("Editing Mode"),
+ NULL,
+ N_("Editing Mode"),
+ NULL, NULL, NULL, NULL },
+
+ { "EHTMLEditor::paragraph-style",
+ NULL,
+ N_("Paragraph Style"),
+ NULL,
+ N_("Paragraph Style"),
+ NULL, NULL, NULL, NULL },
+
+ { "EHTMLEditor::font-name",
+ NULL,
+ N_("Font Name"),
+ NULL,
+ N_("Font Name"),
+ NULL, NULL, NULL, NULL },
+
+ { "EHTMLEditor::font-size",
+ NULL,
+ N_("Font Size"),
+ NULL,
+ N_("Font Size"),
+ NULL, NULL, NULL, NULL },
+
+ { "EHTMLEditor::font-color",
+ NULL,
+ N_("Font Color"),
+ NULL,
+ N_("Font Color"),
+ NULL, NULL, NULL, NULL },
+
+ { "EHTMLEditor::background-color",
+ NULL,
+ N_("Background Color"),
+ NULL,
+ N_("Background Color"),
+ NULL, NULL, NULL, NULL }
};
-static GtkActionEntry core_editor_entries[] = {
+static const EUIActionEntry core_editor_entries[] = {
{ "indent",
"format-indent-more",
N_("_Increase Indent"),
"bracketright",
N_("Increase Indent"),
- G_CALLBACK (action_indent_cb) },
+ action_indent_cb, NULL, NULL, NULL },
{ "insert-emoji",
NULL,
N_("E_moji"),
NULL,
N_("Insert Emoji"),
- G_CALLBACK (action_insert_emoji_cb) },
+ action_insert_emoji_cb, NULL, NULL, NULL },
+
+ { "EHTMLEditor::insert-emoticon",
+ "face-smile",
+ N_("_Emoticon"),
+ NULL,
+ N_("Insert Emoticon"),
+ action_insert_emoticon_cb, "s", NULL, NULL },
{ "insert-emoji-toolbar",
"face-smile",
N_("Insert E_moji"),
NULL,
N_("Insert Emoji"),
- G_CALLBACK (action_insert_emoji_cb) },
+ action_insert_emoji_cb, NULL, NULL, NULL },
{ "insert-html-file",
NULL,
N_("_HTML File…"),
NULL,
NULL,
- G_CALLBACK (action_insert_html_file_cb) },
+ action_insert_html_file_cb, NULL, NULL, NULL },
{ "insert-text-file",
NULL,
N_("Te_xt File…"),
NULL,
NULL,
- G_CALLBACK (action_insert_text_file_cb) },
+ action_insert_text_file_cb, NULL, NULL, NULL },
{ "paste-quote",
NULL,
N_("Paste _Quotation"),
"v",
NULL,
- G_CALLBACK (action_paste_quote_cb) },
+ action_paste_quote_cb, NULL, NULL, NULL },
{ "show-find",
"edit-find",
N_("_Find…"),
"f",
N_("Search for text"),
- G_CALLBACK (action_show_find_cb) },
+ action_show_find_cb, NULL, NULL, NULL },
{ "find-again",
NULL,
N_("Find A_gain"),
"g",
NULL,
- G_CALLBACK (action_find_again_cb) },
+ action_find_again_cb, NULL, NULL, NULL },
{ "show-replace",
"edit-find-replace",
N_("Re_place…"),
"h",
N_("Search for and replace text"),
- G_CALLBACK (action_show_replace_cb) },
+ action_show_replace_cb, NULL, NULL, NULL },
{ "spell-check",
"tools-check-spelling",
N_("Check _Spelling…"),
"F7",
NULL,
- G_CALLBACK (action_spell_check_cb) },
+ action_spell_check_cb, NULL, NULL, NULL },
{ "unindent",
"format-indent-less",
N_("_Decrease Indent"),
"bracketleft",
N_("Decrease Indent"),
- G_CALLBACK (action_unindent_cb) },
+ action_unindent_cb, NULL, NULL, NULL },
{ "wrap-lines",
NULL,
N_("_Wrap Lines"),
"k",
NULL,
- G_CALLBACK (action_wrap_lines_cb) }
+ action_wrap_lines_cb, NULL, NULL, NULL }
};
-static GtkRadioActionEntry core_justify_entries[] = {
+static const EUIActionEnumEntry core_justify_entries[] = {
{ "justify-center",
"format-justify-center",
N_("_Center"),
"e",
N_("Center Alignment"),
- E_CONTENT_EDITOR_ALIGNMENT_CENTER },
+ NULL, E_CONTENT_EDITOR_ALIGNMENT_CENTER },
{ "justify-fill",
"format-justify-fill",
N_("_Justified"),
"j",
N_("Align Justified"),
- E_CONTENT_EDITOR_ALIGNMENT_JUSTIFY },
+ NULL, E_CONTENT_EDITOR_ALIGNMENT_JUSTIFY },
{ "justify-left",
"format-justify-left",
N_("_Left"),
"l",
N_("Left Alignment"),
- E_CONTENT_EDITOR_ALIGNMENT_LEFT },
+ NULL, E_CONTENT_EDITOR_ALIGNMENT_LEFT },
{ "justify-right",
"format-justify-right",
N_("_Right"),
"r",
N_("Right Alignment"),
- E_CONTENT_EDITOR_ALIGNMENT_RIGHT }
+ NULL, E_CONTENT_EDITOR_ALIGNMENT_RIGHT }
};
-static GtkRadioActionEntry core_mode_entries[] = {
+static const EUIActionEnumEntry core_mode_entries[] = {
{ "mode-html",
NULL,
N_("_HTML"),
NULL,
N_("HTML editing mode"),
- E_CONTENT_EDITOR_MODE_HTML },
+ NULL, E_CONTENT_EDITOR_MODE_HTML },
{ "mode-plain",
NULL,
N_("Plain _Text"),
NULL,
N_("Plain text editing mode"),
- E_CONTENT_EDITOR_MODE_PLAIN_TEXT },
+ NULL, E_CONTENT_EDITOR_MODE_PLAIN_TEXT },
{ "mode-markdown",
NULL,
N_("_Markdown"),
NULL,
N_("Markdown editing mode"),
- E_CONTENT_EDITOR_MODE_MARKDOWN },
+ NULL, E_CONTENT_EDITOR_MODE_MARKDOWN },
{ "mode-markdown-plain",
NULL,
N_("Ma_rkdown as Plain Text"),
NULL,
N_("Markdown editing mode, exported as Plain Text"),
- E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT },
+ NULL, E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT },
{ "mode-markdown-html",
NULL,
N_("Mar_kdown as HTML"),
NULL,
N_("Markdown editing mode, exported as HTML"),
- E_CONTENT_EDITOR_MODE_MARKDOWN_HTML }
+ NULL, E_CONTENT_EDITOR_MODE_MARKDOWN_HTML }
};
-static GtkRadioActionEntry core_style_entries[] = {
+static const EUIActionEnumEntry core_style_entries[] = {
{ "style-normal",
NULL,
N_("_Normal"),
"0",
NULL,
- E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH },
+ NULL, E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH },
{ "style-h1",
NULL,
N_("Heading _1"),
"1",
NULL,
- E_CONTENT_EDITOR_BLOCK_FORMAT_H1 },
+ NULL, E_CONTENT_EDITOR_BLOCK_FORMAT_H1 },
{ "style-h2",
NULL,
N_("Heading _2"),
"2",
NULL,
- E_CONTENT_EDITOR_BLOCK_FORMAT_H2 },
+ NULL, E_CONTENT_EDITOR_BLOCK_FORMAT_H2 },
{ "style-h3",
NULL,
N_("Heading _3"),
"3",
NULL,
- E_CONTENT_EDITOR_BLOCK_FORMAT_H3 },
+ NULL, E_CONTENT_EDITOR_BLOCK_FORMAT_H3 },
{ "style-h4",
NULL,
N_("Heading _4"),
"4",
NULL,
- E_CONTENT_EDITOR_BLOCK_FORMAT_H4 },
+ NULL, E_CONTENT_EDITOR_BLOCK_FORMAT_H4 },
{ "style-h5",
NULL,
N_("Heading _5"),
"5",
NULL,
- E_CONTENT_EDITOR_BLOCK_FORMAT_H5 },
+ NULL, E_CONTENT_EDITOR_BLOCK_FORMAT_H5 },
{ "style-h6",
NULL,
N_("Heading _6"),
"6",
NULL,
- E_CONTENT_EDITOR_BLOCK_FORMAT_H6 },
+ NULL, E_CONTENT_EDITOR_BLOCK_FORMAT_H6 },
{ "style-preformat",
NULL,
N_("_Preformatted"),
"7",
NULL,
- E_CONTENT_EDITOR_BLOCK_FORMAT_PRE },
+ NULL, E_CONTENT_EDITOR_BLOCK_FORMAT_PRE },
{ "style-address",
NULL,
N_("A_ddress"),
"8",
NULL,
- E_CONTENT_EDITOR_BLOCK_FORMAT_ADDRESS },
+ NULL, E_CONTENT_EDITOR_BLOCK_FORMAT_ADDRESS },
{ "style-list-bullet",
NULL,
N_("_Bulleted List"),
NULL,
NULL,
- E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST },
+ NULL, E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST },
{ "style-list-roman",
NULL,
N_("_Roman Numeral List"),
NULL,
NULL,
- E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ROMAN },
+ NULL, E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ROMAN },
{ "style-list-number",
NULL,
N_("Numbered _List"),
NULL,
NULL,
- E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST },
+ NULL, E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST },
{ "style-list-alpha",
NULL,
N_("_Alphabetical List"),
NULL,
NULL,
- E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA }
+ NULL, E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA }
};
/*****************************************************************************
@@ -1496,7 +1695,7 @@ static GtkRadioActionEntry core_style_entries[] = {
* These actions are only enabled in HTML mode.
*****************************************************************************/
-static GtkActionEntry html_entries[] = {
+static const EUIActionEntry html_entries[] = {
{ "insert-image",
"insert-image",
@@ -1504,14 +1703,14 @@ static GtkActionEntry html_entries[] = {
NULL,
/* Translators: This is an action tooltip */
N_("Insert Image"),
- G_CALLBACK (action_insert_image_cb) },
+ action_insert_image_cb, NULL, NULL, NULL },
{ "insert-link",
"insert-link",
N_("_Link…"),
"k",
N_("Insert Link"),
- G_CALLBACK (action_insert_link_cb) },
+ action_insert_link_cb, NULL, NULL, NULL },
{ "insert-rule",
"stock_insert-rule",
@@ -1520,42 +1719,42 @@ static GtkActionEntry html_entries[] = {
NULL,
/* Translators: 'Rule' here means a horizontal line in an HTML text */
N_("Insert Rule"),
- G_CALLBACK (action_insert_rule_cb) },
+ action_insert_rule_cb, NULL, NULL, NULL },
{ "insert-table",
"stock_insert-table",
N_("_Table…"),
NULL,
N_("Insert Table"),
- G_CALLBACK (action_insert_table_cb) },
+ action_insert_table_cb, NULL, NULL, NULL },
{ "properties-cell",
NULL,
N_("_Cell…"),
NULL,
NULL,
- G_CALLBACK (action_properties_cell_cb) },
+ action_properties_cell_cb, NULL, NULL, NULL },
{ "properties-image",
NULL,
N_("_Image…"),
NULL,
NULL,
- G_CALLBACK (action_properties_image_cb) },
+ action_properties_image_cb, NULL, NULL, NULL },
{ "properties-link",
NULL,
N_("_Link…"),
NULL,
NULL,
- G_CALLBACK (action_properties_link_cb) },
+ action_properties_link_cb, NULL, NULL, NULL },
{ "properties-page",
NULL,
N_("Pa_ge…"),
NULL,
NULL,
- G_CALLBACK (action_properties_page_cb) },
+ action_properties_page_cb, NULL, NULL, NULL },
{ "properties-rule",
NULL,
@@ -1563,14 +1762,14 @@ static GtkActionEntry html_entries[] = {
N_("_Rule…"),
NULL,
NULL,
- G_CALLBACK (action_properties_rule_cb) },
+ action_properties_rule_cb, NULL, NULL, NULL },
{ "properties-table",
NULL,
N_("_Table…"),
NULL,
NULL,
- G_CALLBACK (action_properties_table_cb) },
+ action_properties_table_cb, NULL, NULL, NULL },
/* Menus */
@@ -1579,76 +1778,70 @@ static GtkActionEntry html_entries[] = {
N_("Font _Size"),
NULL,
NULL,
- NULL },
+ NULL, NULL, NULL, NULL },
{ "font-style-menu",
NULL,
N_("_Font Style"),
NULL,
NULL,
- NULL },
+ NULL, NULL, NULL, NULL },
{ "paste-as-text",
NULL,
N_("Paste As _Text"),
"v",
NULL,
- G_CALLBACK (action_paste_as_text_cb) },
+ action_paste_as_text_cb, NULL, NULL, NULL },
};
-static GtkToggleActionEntry html_toggle_entries[] = {
+static const EUIActionEntry html_toggle_entries[] = {
{ "bold",
"format-text-bold",
N_("_Bold"),
"b",
N_("Bold"),
- NULL,
- FALSE },
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
{ "italic",
"format-text-italic",
N_("_Italic"),
"i",
N_("Italic"),
- NULL,
- FALSE },
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
{ "strikethrough",
"format-text-strikethrough",
N_("_Strikethrough"),
NULL,
N_("Strikethrough"),
- NULL,
- FALSE },
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
{ "subscript",
NULL,
N_("Subs_cript"),
"b",
N_("Subscript"),
- NULL,
- FALSE },
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
{ "superscript",
NULL,
N_("Su_perscript"),
"p",
N_("Superscript"),
- NULL,
- FALSE },
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state },
{ "underline",
"format-text-underline",
N_("_Underline"),
"u",
N_("Underline"),
- NULL,
- FALSE }
+ NULL, NULL, "false", (EUIActionFunc) e_ui_action_set_state }
};
-static GtkRadioActionEntry html_size_entries[] = {
+static const EUIActionEnumEntry html_size_entries[] = {
{ "size-minus-two",
NULL,
@@ -1656,7 +1849,7 @@ static GtkRadioActionEntry html_size_entries[] = {
N_("-2"),
NULL,
NULL,
- E_CONTENT_EDITOR_FONT_SIZE_TINY },
+ NULL, E_CONTENT_EDITOR_FONT_SIZE_TINY },
{ "size-minus-one",
NULL,
@@ -1664,7 +1857,7 @@ static GtkRadioActionEntry html_size_entries[] = {
N_("-1"),
NULL,
NULL,
- E_CONTENT_EDITOR_FONT_SIZE_SMALL },
+ NULL, E_CONTENT_EDITOR_FONT_SIZE_SMALL },
{ "size-plus-zero",
NULL,
@@ -1672,7 +1865,7 @@ static GtkRadioActionEntry html_size_entries[] = {
N_("+0"),
NULL,
NULL,
- E_CONTENT_EDITOR_FONT_SIZE_NORMAL },
+ NULL, E_CONTENT_EDITOR_FONT_SIZE_NORMAL },
{ "size-plus-one",
NULL,
@@ -1680,7 +1873,7 @@ static GtkRadioActionEntry html_size_entries[] = {
N_("+1"),
NULL,
NULL,
- E_CONTENT_EDITOR_FONT_SIZE_BIG },
+ NULL, E_CONTENT_EDITOR_FONT_SIZE_BIG },
{ "size-plus-two",
NULL,
@@ -1688,7 +1881,7 @@ static GtkRadioActionEntry html_size_entries[] = {
N_("+2"),
NULL,
NULL,
- E_CONTENT_EDITOR_FONT_SIZE_BIGGER },
+ NULL, E_CONTENT_EDITOR_FONT_SIZE_BIGGER },
{ "size-plus-three",
NULL,
@@ -1696,7 +1889,7 @@ static GtkRadioActionEntry html_size_entries[] = {
N_("+3"),
NULL,
NULL,
- E_CONTENT_EDITOR_FONT_SIZE_LARGE },
+ NULL, E_CONTENT_EDITOR_FONT_SIZE_LARGE },
{ "size-plus-four",
NULL,
@@ -1704,7 +1897,7 @@ static GtkRadioActionEntry html_size_entries[] = {
N_("+4"),
NULL,
NULL,
- E_CONTENT_EDITOR_FONT_SIZE_VERY_LARGE }
+ NULL, E_CONTENT_EDITOR_FONT_SIZE_VERY_LARGE }
};
/*****************************************************************************
@@ -1716,49 +1909,49 @@ static GtkRadioActionEntry html_size_entries[] = {
* non-context sensitive counterparts.
*****************************************************************************/
-static GtkActionEntry context_entries[] = {
+static const EUIActionEntry context_entries[] = {
{ "context-copy-link",
"edit-copy",
N_("Copy _Link Location"),
NULL,
N_("Copy the link to the clipboard"),
- G_CALLBACK (action_copy_link_cb) },
+ action_copy_link_cb, NULL, NULL, NULL },
{ "context-open-link",
"emblem-web",
N_("_Open Link in Browser"),
NULL,
N_("Open the link in a web browser"),
- G_CALLBACK (action_open_link_cb) },
+ action_open_link_cb, NULL, NULL, NULL },
{ "context-delete-cell",
NULL,
N_("Cell Contents"),
NULL,
NULL,
- G_CALLBACK (action_context_delete_cell_contents_cb) },
+ action_context_delete_cell_contents_cb, NULL, NULL, NULL },
{ "context-delete-column",
NULL,
N_("Column"),
NULL,
NULL,
- G_CALLBACK (action_context_delete_column_cb) },
+ action_context_delete_column_cb, NULL, NULL, NULL },
{ "context-delete-row",
NULL,
N_("Row"),
NULL,
NULL,
- G_CALLBACK (action_context_delete_row_cb) },
+ action_context_delete_row_cb, NULL, NULL, NULL },
{ "context-delete-table",
NULL,
N_("Table"),
NULL,
NULL,
- G_CALLBACK (action_context_delete_table_cb) },
+ action_context_delete_table_cb, NULL, NULL, NULL },
/* Menus */
@@ -1768,7 +1961,7 @@ static GtkActionEntry context_entries[] = {
N_("Table Delete"),
NULL,
NULL,
- NULL },
+ NULL, NULL, NULL, NULL },
{ "context-insert-table-menu",
NULL,
@@ -1776,14 +1969,14 @@ static GtkActionEntry context_entries[] = {
N_("Table Insert"),
NULL,
NULL,
- NULL },
+ NULL, NULL, NULL, NULL },
{ "context-properties-menu",
NULL,
N_("Properties"),
NULL,
NULL,
- NULL },
+ NULL, NULL, NULL, NULL }
};
/*****************************************************************************
@@ -1794,91 +1987,91 @@ static GtkActionEntry context_entries[] = {
* counterparts.
*****************************************************************************/
-static GtkActionEntry html_context_entries[] = {
+static const EUIActionEntry html_context_entries[] = {
{ "context-delete-hrule",
NULL,
N_("Delete Rule"),
NULL,
NULL,
- G_CALLBACK (action_context_delete_hrule_cb) },
+ action_context_delete_hrule_cb, NULL, NULL, NULL },
{ "context-delete-image",
NULL,
N_("Delete Image"),
NULL,
NULL,
- G_CALLBACK (action_context_delete_image_cb) },
+ action_context_delete_image_cb, NULL, NULL, NULL },
{ "context-insert-column-after",
NULL,
N_("Column After"),
NULL,
NULL,
- G_CALLBACK (action_context_insert_column_after_cb) },
+ action_context_insert_column_after_cb, NULL, NULL, NULL },
{ "context-insert-column-before",
NULL,
N_("Column Before"),
NULL,
NULL,
- G_CALLBACK (action_context_insert_column_before_cb) },
+ action_context_insert_column_before_cb, NULL, NULL, NULL },
{ "context-insert-link",
NULL,
N_("Insert _Link"),
NULL,
NULL,
- G_CALLBACK (action_insert_link_cb) },
+ action_insert_link_cb, NULL, NULL, NULL },
{ "context-insert-row-above",
NULL,
N_("Row Above"),
NULL,
NULL,
- G_CALLBACK (action_context_insert_row_above_cb) },
+ action_context_insert_row_above_cb, NULL, NULL, NULL },
{ "context-insert-row-below",
NULL,
N_("Row Below"),
NULL,
NULL,
- G_CALLBACK (action_context_insert_row_below_cb) },
+ action_context_insert_row_below_cb, NULL, NULL, NULL },
{ "context-properties-cell",
NULL,
N_("Cell…"),
NULL,
NULL,
- G_CALLBACK (action_properties_cell_cb) },
+ action_properties_cell_cb, NULL, NULL, NULL },
{ "context-properties-image",
NULL,
N_("Image…"),
NULL,
NULL,
- G_CALLBACK (action_properties_image_cb) },
+ action_properties_image_cb, NULL, NULL, NULL },
{ "context-properties-link",
NULL,
N_("Link…"),
NULL,
NULL,
- G_CALLBACK (action_properties_link_cb) },
+ action_properties_link_cb, NULL, NULL, NULL },
{ "context-properties-page",
NULL,
N_("Page…"),
NULL,
NULL,
- G_CALLBACK (action_properties_page_cb) },
+ action_properties_page_cb, NULL, NULL, NULL },
{ "context-properties-paragraph",
NULL,
N_("Paragraph…"),
NULL,
NULL,
- G_CALLBACK (action_properties_paragraph_cb) },
+ action_properties_paragraph_cb, NULL, NULL, NULL },
{ "context-properties-rule",
NULL,
@@ -1886,28 +2079,28 @@ static GtkActionEntry html_context_entries[] = {
N_("Rule…"),
NULL,
NULL,
- G_CALLBACK (action_properties_rule_cb) },
+ action_properties_rule_cb, NULL, NULL, NULL },
{ "context-properties-table",
NULL,
N_("Table…"),
NULL,
NULL,
- G_CALLBACK (action_properties_table_cb) },
+ action_properties_table_cb, NULL, NULL, NULL },
{ "context-properties-text",
NULL,
N_("Text…"),
NULL,
NULL,
- G_CALLBACK (action_properties_text_cb) },
+ action_properties_text_cb, NULL, NULL, NULL },
{ "context-remove-link",
NULL,
N_("Remove Link"),
NULL,
NULL,
- G_CALLBACK (action_context_remove_link_cb) }
+ action_context_remove_link_cb, NULL, NULL, NULL }
};
/*****************************************************************************
@@ -1917,78 +2110,126 @@ static GtkActionEntry html_context_entries[] = {
* misspelled.
*****************************************************************************/
-static GtkActionEntry spell_context_entries[] = {
+static const EUIActionEntry spell_context_entries[] = {
{ "context-spell-add",
NULL,
N_("Add Word to Dictionary"),
NULL,
NULL,
- G_CALLBACK (action_context_spell_add_cb) },
+ action_context_spell_add_cb, NULL, NULL, NULL },
{ "context-spell-ignore",
NULL,
N_("Ignore Misspelled Word"),
NULL,
NULL,
- G_CALLBACK (action_context_spell_ignore_cb) },
+ action_context_spell_ignore_cb, NULL, NULL, NULL },
- { "context-spell-add-menu",
+ { "EHTMLEditor::context-spell-add-menu",
NULL,
N_("Add Word To"),
NULL,
NULL,
- NULL },
+ NULL, NULL, NULL, NULL },
/* Menus */
- { "context-more-suggestions-menu",
+ { "EHTMLEditor::context-spell-suggest",
+ NULL,
+ N_("Spell check suggestions"),
+ NULL,
+ NULL,
+ NULL, NULL, NULL, NULL },
+
+ { "EHTMLEditor::context-spell-suggest-more-menu",
NULL,
N_("More Suggestions"),
NULL,
NULL,
- NULL }
+ NULL, NULL, NULL, NULL }
};
+static void
+editor_actions_setup_emoticon_menu (EHTMLEditor *editor)
+{
+ GList *list, *link;
+
+ list = e_emoticon_chooser_get_items ();
+
+ for (link = list; link; link = g_list_next (link)) {
+ EEmoticon *emoticon = link->data;
+ GMenuItem *item;
+
+ item = g_menu_item_new (_(emoticon->label), NULL);
+ g_menu_item_set_attribute (item, G_MENU_ATTRIBUTE_ACTION, "s", "core-editor.EHTMLEditor::insert-emoticon");
+ g_menu_item_set_attribute (item, G_MENU_ATTRIBUTE_TARGET, "s", emoticon->icon_name);
+ g_menu_item_set_attribute (item, G_MENU_ATTRIBUTE_ICON, "s", emoticon->icon_name);
+
+ g_menu_append_item (editor->priv->emoticon_menu, item);
+
+ g_clear_object (&item);
+ }
+
+ g_list_free (list);
+}
+
static gboolean
editor_actions_add_to_recent_languages (EHTMLEditor *editor,
const gchar *language_code)
{
- GtkAction *language_action;
- gchar *name;
+ EUIAction *language_action;
+ GMenuModel *menu_model;
+ GMenuItem *menu_item;
+ const gchar *language_action_name;
+ guint ii, n_items;
+ guint group_name_len_plus_one, action_name_len;
g_return_val_if_fail (E_IS_HTML_EDITOR (editor), FALSE);
g_return_val_if_fail (language_code != NULL, FALSE);
- language_action = gtk_action_group_get_action (editor->priv->language_actions, language_code);
+ language_action = e_ui_action_group_get_action (editor->priv->language_actions, language_code);
if (!language_action)
return FALSE;
- name = g_strconcat ("recent-spell-language-", language_code, NULL);
+ group_name_len_plus_one = strlen (e_ui_action_group_get_name (editor->priv->language_actions)) + 1;
+ action_name_len = strlen (g_action_get_name (G_ACTION (language_action)));
+ language_action_name = g_action_get_name (G_ACTION (language_action));
- if (!gtk_action_group_get_action (editor->priv->language_actions, name)) {
- GtkToggleAction *toggle_action;
+ menu_model = G_MENU_MODEL (editor->priv->recent_languages_menu);
+ n_items = g_menu_model_get_n_items (menu_model);
+ for (ii = 0; ii < n_items; ii++) {
+ GVariant *value;
+ const gchar *action_full_name;
- toggle_action = gtk_toggle_action_new (name,
- gtk_action_get_label (language_action),
- gtk_action_get_tooltip (language_action),
- NULL);
+ value = g_menu_model_get_item_attribute_value (menu_model, ii, G_MENU_ATTRIBUTE_ACTION, G_VARIANT_TYPE_STRING);
+ if (!value || !g_variant_get_string (value, NULL)) {
+ g_clear_pointer (&value, g_variant_unref);
+ continue;
+ }
- e_binding_bind_property (language_action, "active",
- toggle_action, "active",
- G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ action_full_name = g_variant_get_string (value, NULL);
- gtk_action_group_add_action (editor->priv->language_actions, GTK_ACTION (toggle_action));
+ if (strlen (action_full_name) == group_name_len_plus_one + action_name_len &&
+ g_str_has_suffix (action_full_name, language_action_name) &&
+ g_str_has_prefix (action_full_name, e_ui_action_group_get_name (editor->priv->language_actions))) {
+ g_clear_pointer (&value, g_variant_unref);
+ break;
+ }
- g_object_unref (toggle_action);
+ g_clear_pointer (&value, g_variant_unref);
}
- gtk_ui_manager_add_ui (
- editor->priv->manager, editor->priv->recent_spell_languages_merge_id,
- "/main-menu/edit-menu/language-menu/recent-languages",
- name, name, GTK_UI_MANAGER_AUTO, FALSE);
+ /* it's already in the list */
+ if (ii < n_items)
+ return TRUE;
- g_free (name);
+ menu_item = g_menu_item_new (NULL, NULL);
+ e_ui_manager_update_item_from_action (editor->priv->ui_manager, menu_item, language_action);
+
+ g_menu_append_item (editor->priv->recent_languages_menu, menu_item);
+
+ g_clear_object (&menu_item);
return TRUE;
}
@@ -1998,32 +2239,30 @@ editor_actions_setup_languages_menu (EHTMLEditor *editor)
{
ESpellChecker *spell_checker;
EContentEditor *cnt_editor;
- GtkUIManager *manager;
- GtkActionGroup *action_group;
- GHashTable *lang_parents; /* gchar *name ~> GtkAction * */
+ EUIActionGroup *action_group;
+ GHashTable *lang_parents; /* gchar *name ~> GMenu * (to add variants into a submenu) */
GList *list = NULL, *link;
GSettings *settings;
gchar **strv;
+ const gchar *map_name;
gint ii, added = 0, max_items;
- guint merge_id;
lang_parents = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
- manager = editor->priv->manager;
action_group = editor->priv->language_actions;
+ map_name = e_ui_action_group_get_name (action_group);
cnt_editor = e_html_editor_get_content_editor (editor);
spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
- merge_id = gtk_ui_manager_new_merge_id (manager);
- editor->priv->recent_spell_languages_merge_id = gtk_ui_manager_new_merge_id (manager);
+
+ g_menu_remove_all (editor->priv->all_languages_menu);
list = e_spell_checker_list_available_dicts (spell_checker);
for (link = list; link != NULL; link = g_list_next (link)) {
ESpellDictionary *dictionary = link->data;
- GtkAction *parent_action;
- GtkToggleAction *action;
- const gchar *dictionay_name;
- gchar *language_name, *path;
- GString *escaped_name = NULL;
+ GMenu *parent_lang_menu;
+ GMenuItem *menu_item;
+ EUIAction *action;
+ gchar *language_name;
gboolean active = FALSE;
if (!e_util_get_language_info (e_spell_dictionary_get_code (dictionary), &language_name, NULL)) {
@@ -2039,72 +2278,66 @@ editor_actions_setup_languages_menu (EHTMLEditor *editor)
}
}
- dictionay_name = e_spell_dictionary_get_name (dictionary);
- if (dictionay_name && strchr (dictionay_name, '_') != NULL)
- escaped_name = e_str_replace_string (dictionay_name, "_", "__");
+ action = e_ui_action_group_get_action (action_group, e_spell_dictionary_get_code (dictionary));
+ if (action) {
+ g_object_ref (action);
+ } else {
+ const gchar *dictionay_name;
+ GString *escaped_name = NULL;
- action = gtk_toggle_action_new (
- e_spell_dictionary_get_code (dictionary),
- escaped_name ? escaped_name->str : dictionay_name,
- NULL, NULL);
+ dictionay_name = e_spell_dictionary_get_name (dictionary);
+ if (dictionay_name && strchr (dictionay_name, '_') != NULL)
+ escaped_name = e_str_replace_string (dictionay_name, "_", "__");
- if (escaped_name)
- g_string_free (escaped_name, TRUE);
+ action = e_ui_action_new_stateful (map_name,
+ e_spell_dictionary_get_code (dictionary), NULL,
+ g_variant_new_boolean (FALSE));
+ e_ui_action_set_label (action, escaped_name ? escaped_name->str : dictionay_name);
- /* Do this BEFORE connecting to the "toggled" signal.
- * We're not prepared to invoke the signal handler yet.
- * The "Add Word To" actions have not yet been added. */
- active = e_spell_checker_get_language_active (
- spell_checker, e_spell_dictionary_get_code (dictionary));
- gtk_toggle_action_set_active (action, active);
+ if (escaped_name)
+ g_string_free (escaped_name, TRUE);
- g_signal_connect (
- action, "toggled",
- G_CALLBACK (action_language_cb), editor);
+ g_signal_connect_object (
+ action, "change-state",
+ G_CALLBACK (e_ui_action_set_state), editor, 0);
- gtk_action_group_add_action (
- action_group, GTK_ACTION (action));
+ g_signal_connect_object (
+ action, "notify::active",
+ G_CALLBACK (action_language_notify_active_cb), editor, 0);
- g_object_unref (action);
-
- parent_action = g_hash_table_lookup (lang_parents, language_name);
- if (!parent_action) {
- gchar *name, *tmp;
-
- name = g_strdup (e_spell_dictionary_get_code (dictionary));
- tmp = strchr (name, '_');
- if (tmp)
- *tmp = '\0';
-
- tmp = g_strconcat ("language-parent-", name, NULL);
- g_free (name);
- name = tmp;
-
- parent_action = gtk_action_new (name, language_name, NULL, NULL);
-
- gtk_action_group_add_action (action_group, parent_action);
-
- g_hash_table_insert (lang_parents, g_strdup (language_name), parent_action);
-
- gtk_ui_manager_add_ui (
- manager, merge_id,
- "/main-menu/edit-menu/language-menu/all-languages",
- name, name, GTK_UI_MANAGER_MENU, FALSE);
-
- g_free (name);
+ e_ui_action_group_add (action_group, action);
+ e_ui_menu_track_action (editor->priv->main_menu, action);
}
- path = g_strconcat ("/main-menu/edit-menu/language-menu/all-languages/", gtk_action_get_name (parent_action), NULL);
+ active = e_spell_checker_get_language_active (spell_checker, e_spell_dictionary_get_code (dictionary));
- gtk_ui_manager_add_ui (
- manager, merge_id,
- path,
- e_spell_dictionary_get_code (dictionary),
- e_spell_dictionary_get_code (dictionary),
- GTK_UI_MANAGER_AUTO, FALSE);
+ if ((!e_ui_action_get_active (action)) != (!active)) {
+ g_signal_handlers_block_by_func (action, action_language_notify_active_cb, editor);
+ e_ui_action_set_active (action, active);
+ g_signal_handlers_unblock_by_func (action, action_language_notify_active_cb, editor);
+ }
+ parent_lang_menu = g_hash_table_lookup (lang_parents, language_name);
+ if (!parent_lang_menu) {
+ parent_lang_menu = g_menu_new ();
+
+ menu_item = g_menu_item_new_submenu (language_name, G_MENU_MODEL (parent_lang_menu));
+
+ g_hash_table_insert (lang_parents, g_strdup (language_name), parent_lang_menu);
+
+ g_menu_append_item (editor->priv->all_languages_menu, menu_item);
+
+ g_clear_object (&menu_item);
+ }
+
+ menu_item = g_menu_item_new (NULL, NULL);
+ e_ui_manager_update_item_from_action (editor->priv->ui_manager, menu_item, action);
+
+ g_menu_append_item (parent_lang_menu, menu_item);
+
+ g_clear_object (&menu_item);
+ g_clear_object (&action);
g_free (language_name);
- g_free (path);
}
g_list_free (list);
@@ -2131,77 +2364,69 @@ editor_actions_setup_spell_check_menu (EHTMLEditor *editor)
{
EContentEditor *cnt_editor;
ESpellChecker *spell_checker;
- GtkUIManager *manager;
- GtkActionGroup *action_group;
+ EUIActionGroup *action_group;
GList *available_dicts = NULL, *iter;
- guint merge_id;
+ const gchar *map_name;
- manager = editor->priv->manager;
action_group = editor->priv->spell_check_actions;
+ map_name = e_ui_action_group_get_name (action_group);
cnt_editor = e_html_editor_get_content_editor (editor);
spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
available_dicts = e_spell_checker_list_available_dicts (spell_checker);
- merge_id = gtk_ui_manager_new_merge_id (manager);
for (iter = available_dicts; iter; iter = iter->next) {
ESpellDictionary *dictionary = iter->data;
- GtkAction *action;
+ EUIAction *action;
+ GMenu *menu;
const gchar *code;
const gchar *name;
GString *escaped_name = NULL;
gchar *action_label;
- gchar *action_name;
+ gchar action_name[128];
code = e_spell_dictionary_get_code (dictionary);
name = e_spell_dictionary_get_name (dictionary);
/* Add a suggestion menu. */
- action_name = g_strdup_printf (
- "context-spell-suggest-%s-menu", code);
+ g_warn_if_fail (g_snprintf (action_name, sizeof (action_name), "context-spell-suggest-%s-menu", code) < sizeof (action_name));
if (name && strchr (name, '_') != NULL)
escaped_name = e_str_replace_string (name, "_", "__");
- action = gtk_action_new (action_name, escaped_name ? escaped_name->str : name, NULL, NULL);
- gtk_action_group_add_action (action_group, action);
- g_object_unref (action);
+ action = e_ui_action_new (map_name, action_name, NULL);
+ e_ui_action_set_label (action, escaped_name ? escaped_name->str : name);
+ e_ui_action_set_visible (action, FALSE);
+ e_ui_action_group_add (action_group, action);
- gtk_ui_manager_add_ui (
- manager, merge_id,
- "/context-menu/context-spell-suggest",
- action_name, action_name,
- GTK_UI_MANAGER_MENU, FALSE);
+ g_ptr_array_add (editor->priv->spell_suggest_actions, g_object_ref (action));
- g_free (action_name);
+ menu = g_menu_new ();
+
+ g_hash_table_insert (editor->priv->spell_suggest_menus_by_code, g_strdup (code),
+ /* assumes ownership of both */
+ e_html_editor_action_menu_pair_new (action, menu));
/* Add an item to the "Add Word To" menu. */
- action_name = g_strdup_printf ("context-spell-add-%s", code);
+ g_warn_if_fail (g_snprintf (action_name, sizeof (action_name), "context-spell-add-%s", code) < sizeof (action_name));
/* Translators: %s will be replaced with the actual dictionary
* name, where a user can add a word to. This is part of an
* "Add Word To" submenu. */
action_label = g_strdup_printf (_("%s Dictionary"), escaped_name ? escaped_name->str : name);
- action = gtk_action_new (
- action_name, action_label, NULL, NULL);
+ action = e_ui_action_new_stateful (map_name, action_name, NULL, g_variant_new_string (code));
+ e_ui_action_set_label (action, action_label);
g_signal_connect (
action, "activate",
- G_CALLBACK (action_context_spell_add_cb), editor);
+ G_CALLBACK (action_context_spell_add_to_dict_cb), editor);
- gtk_action_set_visible (action, e_spell_checker_get_language_active (spell_checker, code));
+ e_ui_action_set_visible (action, e_spell_checker_get_language_active (spell_checker, code));
- gtk_action_group_add_action (action_group, action);
-
- g_object_unref (action);
-
- gtk_ui_manager_add_ui (
- manager, merge_id,
- "/context-menu/context-spell-add-menu",
- action_name, action_name,
- GTK_UI_MANAGER_AUTO, FALSE);
+ e_ui_action_group_add (action_group, action);
+ /* the array assumes ownership of the action */
+ g_ptr_array_add (editor->priv->spell_add_actions, action);
g_free (action_label);
- g_free (action_name);
if (escaped_name)
g_string_free (escaped_name, TRUE);
@@ -2212,176 +2437,94 @@ editor_actions_setup_spell_check_menu (EHTMLEditor *editor)
}
void
-e_html_editor_actions_init (EHTMLEditor *editor)
+e_html_editor_actions_add_actions (EHTMLEditor *editor)
{
- GtkAction *action;
- GtkActionGroup *action_group;
- GtkUIManager *manager;
- const gchar *domain;
+ EUIManager *ui_manager;
+
+ g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
+ ui_manager = e_html_editor_get_ui_manager (editor);
+
+ /* Core Actions */
+ e_ui_manager_add_actions (ui_manager, "core", NULL,
+ core_entries, G_N_ELEMENTS (core_entries), editor);
+ e_ui_manager_add_actions (ui_manager, "core-editor", NULL,
+ core_editor_entries, G_N_ELEMENTS (core_editor_entries), editor);
+ e_ui_manager_add_actions_enum (ui_manager, "core-editor", NULL,
+ core_justify_entries, G_N_ELEMENTS (core_justify_entries), editor);
+ e_ui_manager_add_actions_enum (ui_manager, "core-editor", NULL,
+ core_mode_entries, G_N_ELEMENTS (core_mode_entries), editor);
+ e_ui_manager_add_actions_enum (ui_manager, "core-editor", NULL,
+ core_style_entries, G_N_ELEMENTS (core_style_entries), editor);
+
+ /* Core Actions (HTML only) */
+ e_ui_manager_add_actions (ui_manager, "html", NULL,
+ html_entries, G_N_ELEMENTS (html_entries), editor);
+ e_ui_manager_add_actions (ui_manager, "html", NULL,
+ html_toggle_entries, G_N_ELEMENTS (html_toggle_entries), editor);
+ e_ui_manager_add_actions_enum (ui_manager, "html", NULL,
+ html_size_entries, G_N_ELEMENTS (html_size_entries), editor);
+
+ /* Context Menu Actions */
+ e_ui_manager_add_actions (ui_manager, "core-context", NULL,
+ context_entries, G_N_ELEMENTS (context_entries), editor);
+
+ /* Context Menu Actions (HTML only) */
+ e_ui_manager_add_actions (ui_manager, "html-context", NULL,
+ html_context_entries, G_N_ELEMENTS (html_context_entries), editor);
+
+ /* Context Menu Actions (spell check only) */
+ e_ui_manager_add_actions (ui_manager, "spell-check", NULL,
+ spell_context_entries, G_N_ELEMENTS (spell_context_entries), editor);
+}
+
+void
+e_html_editor_actions_setup_actions (EHTMLEditor *editor)
+{
+ EUIAction *action;
+ EUIManager *ui_manager;
guint ii;
g_return_if_fail (E_IS_HTML_EDITOR (editor));
- manager = e_html_editor_get_ui_manager (editor);
- domain = GETTEXT_PACKAGE;
+ ui_manager = e_html_editor_get_ui_manager (editor);
- /* Core Actions */
- action_group = editor->priv->core_actions;
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_action_group_add_actions (
- action_group, core_entries,
- G_N_ELEMENTS (core_entries), editor);
- gtk_ui_manager_insert_action_group (manager, action_group, 0);
-
- action_group = editor->priv->core_editor_actions;
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_action_group_add_actions (
- action_group, core_editor_entries,
- G_N_ELEMENTS (core_editor_entries), editor);
- gtk_action_group_add_radio_actions (
- action_group, core_justify_entries,
- G_N_ELEMENTS (core_justify_entries),
- E_CONTENT_EDITOR_ALIGNMENT_LEFT,
- NULL, NULL);
- gtk_action_group_add_radio_actions (
- action_group, core_mode_entries,
- G_N_ELEMENTS (core_mode_entries),
- E_CONTENT_EDITOR_MODE_HTML,
- G_CALLBACK (action_mode_cb), editor);
- gtk_action_group_add_radio_actions (
- action_group, core_style_entries,
- G_N_ELEMENTS (core_style_entries),
- E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH,
- NULL, NULL);
- gtk_ui_manager_insert_action_group (manager, action_group, 0);
-
- /* Face Action */
- action = e_emoticon_action_new (
- "insert-emoticon", _("_Emoticon"),
- _("Insert Emoticon"), NULL);
- g_object_set (action, "icon-name", "face-smile", NULL);
- g_signal_connect (
- action, "item-activated",
- G_CALLBACK (action_insert_emoticon_cb), editor);
- gtk_action_group_add_action (action_group, action);
- g_object_unref (action);
-
- /* Core Actions (HTML only) */
- action_group = editor->priv->html_actions;
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_action_group_add_actions (
- action_group, html_entries,
- G_N_ELEMENTS (html_entries), editor);
- gtk_action_group_add_toggle_actions (
- action_group, html_toggle_entries,
- G_N_ELEMENTS (html_toggle_entries), editor);
- gtk_action_group_add_radio_actions (
- action_group, html_size_entries,
- G_N_ELEMENTS (html_size_entries),
- E_CONTENT_EDITOR_FONT_SIZE_NORMAL,
- NULL, NULL);
- gtk_ui_manager_insert_action_group (manager, action_group, 0);
-
- /* Context Menu Actions */
- action_group = editor->priv->context_actions;
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_action_group_add_actions (
- action_group, context_entries,
- G_N_ELEMENTS (context_entries), editor);
- gtk_ui_manager_insert_action_group (manager, action_group, 0);
-
- /* Context Menu Actions (HTML only) */
- action_group = editor->priv->html_context_actions;
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_action_group_add_actions (
- action_group, html_context_entries,
- G_N_ELEMENTS (html_context_entries), editor);
- gtk_ui_manager_insert_action_group (manager, action_group, 0);
-
- /* Context Menu Actions (spell check only) */
- action_group = editor->priv->spell_check_actions;
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_action_group_add_actions (
- action_group, spell_context_entries,
- G_N_ELEMENTS (spell_context_entries), editor);
- gtk_ui_manager_insert_action_group (manager, action_group, 0);
+ editor_actions_setup_emoticon_menu (editor);
/* Language actions are generated dynamically. */
editor_actions_setup_languages_menu (editor);
- action_group = editor->priv->language_actions;
- gtk_ui_manager_insert_action_group (manager, action_group, 0);
/* Some spell check actions are generated dynamically. */
- action_group = editor->priv->suggestion_actions;
editor_actions_setup_spell_check_menu (editor);
- gtk_ui_manager_insert_action_group (manager, action_group, 0);
/* Do this after all language actions are initialized. */
e_html_editor_update_spell_actions (editor);
- /* Fine Tuning */
+ e_ui_action_set_sensitive (ACTION (UNINDENT), FALSE);
+ e_ui_action_set_sensitive (ACTION (FIND_AGAIN), FALSE);
- g_object_set (
- G_OBJECT (ACTION (SHOW_FIND)),
- "short-label", _("_Find"), NULL);
- g_object_set (
- G_OBJECT (ACTION (SHOW_REPLACE)),
- "short-label", _("Re_place"), NULL);
- g_object_set (
- G_OBJECT (ACTION (INSERT_EMOJI)),
- "short-label", _("E_moji"), NULL);
- g_object_set (
- G_OBJECT (ACTION (INSERT_IMAGE)),
- "short-label", _("_Image"), NULL);
- g_object_set (
- G_OBJECT (ACTION (INSERT_LINK)),
- "short-label", _("_Link"), NULL);
- g_object_set (
- G_OBJECT (ACTION (INSERT_RULE)),
- /* Translators: 'Rule' here means a horizontal line in an HTML text */
- "short-label", _("_Rule"), NULL);
- g_object_set (
- G_OBJECT (ACTION (INSERT_TABLE)),
- "short-label", _("_Table"), NULL);
-
- gtk_action_set_sensitive (ACTION (UNINDENT), FALSE);
- gtk_action_set_sensitive (ACTION (FIND_AGAIN), FALSE);
-
- g_signal_connect_object (ACTION (SUBSCRIPT), "toggled",
+ g_signal_connect_object (ACTION (SUBSCRIPT), "notify::active",
G_CALLBACK (html_editor_actions_subscript_toggled_cb), editor, 0);
- g_signal_connect_object (ACTION (SUPERSCRIPT), "toggled",
+ g_signal_connect_object (ACTION (SUPERSCRIPT), "notify::active",
G_CALLBACK (html_editor_actions_superscript_toggled_cb), editor, 0);
g_signal_connect (editor, "notify::mode",
G_CALLBACK (html_editor_actions_notify_mode_cb), NULL);
- action_group = editor->priv->core_editor_actions;
- action = gtk_action_group_get_action (action_group, "mode-html");
- e_binding_bind_property (
+ action = e_ui_manager_get_action (ui_manager, "mode-html");
+ e_binding_bind_property_full (
editor, "mode",
- action, "current-value",
- G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ action, "state",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE,
+ e_ui_action_util_gvalue_to_enum_state,
+ e_ui_action_util_enum_state_to_gvalue, NULL, NULL);
for (ii = 0; ii < G_N_ELEMENTS (core_mode_entries); ii++) {
- action = gtk_action_group_get_action (action_group, core_mode_entries[ii].name);
+ action = e_ui_manager_get_action (ui_manager, core_mode_entries[ii].name);
- gtk_action_set_visible (action, e_html_editor_has_editor_for_mode (editor, core_mode_entries[ii].value));
+ e_ui_action_set_visible (action, e_html_editor_has_editor_for_mode (editor, core_mode_entries[ii].state));
}
}
-static gboolean
-e_html_editor_content_editor_font_name_to_combo_box (GBinding *binding,
- const GValue *from_value,
- GValue *to_value,
- gpointer user_data)
-{
- gchar *id = NULL;
-
- id = e_html_editor_util_dup_font_id (GTK_COMBO_BOX (g_binding_get_target (binding)), g_value_get_string (from_value));
- g_value_take_string (to_value, id ? id : g_strdup (""));
-
- return TRUE;
-}
-
static gboolean
e_html_editor_indent_level_to_bool_indent_cb (GBinding *binding,
const GValue *from_value,
@@ -2438,21 +2581,41 @@ e_html_editor_sensitize_html_actions_cb (GBinding *binding,
EActionComboBox *
e_html_editor_util_new_mode_combobox (void)
{
- GtkActionGroup *action_group;
- GtkAction *action;
+ EUIActionGroup *action_group;
+ EUIAction *action;
+ EUIManager *ui_manager;
+ GPtrArray *actions;
GtkWidget *widget;
- action_group = gtk_action_group_new ("core-mode-entries");
+ ui_manager = e_ui_manager_new ();
- gtk_action_group_add_radio_actions (
- action_group, core_mode_entries,
- G_N_ELEMENTS (core_mode_entries),
- E_CONTENT_EDITOR_MODE_HTML,
- NULL, NULL);
+ e_ui_manager_add_actions_enum (ui_manager, "core-mode-entries", NULL,
+ core_mode_entries, G_N_ELEMENTS (core_mode_entries), NULL);
- action = gtk_action_group_get_action (action_group, "mode-html");
+ action_group = e_ui_manager_get_action_group (ui_manager, "core-mode-entries");
- widget = e_action_combo_box_new_with_action (GTK_RADIO_ACTION (action));
+ g_object_ref (action_group);
+ g_clear_object (&ui_manager);
+
+ actions = e_ui_action_group_list_actions (action_group);
+ if (actions) {
+ GPtrArray *group;
+ guint ii;
+
+ group = g_ptr_array_new ();
+
+ for (ii = 0; ii < actions->len; ii++) {
+ EUIAction *radio_action = g_ptr_array_index (actions, ii);
+ e_ui_action_set_radio_group (radio_action, group);
+ }
+
+ g_ptr_array_unref (group);
+ g_ptr_array_unref (actions);
+ }
+
+ action = e_ui_action_group_get_action (action_group, "mode-html");
+
+ widget = e_action_combo_box_new_with_action (action);
gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (widget), FALSE);
gtk_widget_set_tooltip_text (widget, _("Editing Mode"));
@@ -2473,23 +2636,6 @@ e_html_editor_actions_bind (EHTMLEditor *editor)
/* 'rb' for 'remember binding' */
#define rb(x) editor->priv->content_editor_bindings = g_slist_prepend (editor->priv->content_editor_bindings, g_object_ref (x))
- rb (e_binding_bind_property (
- editor->priv->fg_color_combo_box, "current-color",
- cnt_editor, "font-color",
- G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL));
- rb (e_binding_bind_property (
- cnt_editor, "editable",
- editor->priv->fg_color_combo_box, "sensitive",
- G_BINDING_SYNC_CREATE));
- rb (e_binding_bind_property (
- editor->priv->bg_color_combo_box, "current-color",
- cnt_editor, "background-color",
- G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL));
- rb (e_binding_bind_property (
- cnt_editor, "editable",
- editor->priv->bg_color_combo_box, "sensitive",
- G_BINDING_SYNC_CREATE));
-
rb (e_binding_bind_property (
cnt_editor, "can-redo",
ACTION (REDO), "sensitive",
@@ -2517,22 +2663,28 @@ e_html_editor_actions_bind (EHTMLEditor *editor)
/* This is connected to JUSTIFY_LEFT action only, but
* it automatically applies on all actions in the group. */
- rb (e_binding_bind_property (
+ rb (e_binding_bind_property_full (
cnt_editor, "alignment",
- ACTION (JUSTIFY_LEFT), "current-value",
- G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL));
+ ACTION (JUSTIFY_LEFT), "state",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
+ e_ui_action_util_gvalue_to_enum_state,
+ e_ui_action_util_enum_state_to_gvalue, NULL, NULL));
rb (e_binding_bind_property (
cnt_editor, "bold",
ACTION (BOLD), "active",
G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL));
- rb (e_binding_bind_property (
+ rb (e_binding_bind_property_full (
cnt_editor, "font-size",
- ACTION (FONT_SIZE_GROUP), "current-value",
- G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL));
- rb (e_binding_bind_property (
+ ACTION (FONT_SIZE_GROUP), "state",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
+ e_ui_action_util_gvalue_to_enum_state,
+ e_ui_action_util_enum_state_to_gvalue, NULL, NULL));
+ rb (e_binding_bind_property_full (
cnt_editor, "block-format",
- ACTION (STYLE_NORMAL), "current-value",
- G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL));
+ ACTION (STYLE_NORMAL), "state",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
+ e_ui_action_util_gvalue_to_enum_state,
+ e_ui_action_util_enum_state_to_gvalue, NULL, NULL));
rb (e_binding_bind_property_full (
cnt_editor, "indent-level",
ACTION (INDENT), "sensitive",
@@ -2557,13 +2709,6 @@ e_html_editor_actions_bind (EHTMLEditor *editor)
cnt_editor, "underline",
ACTION (UNDERLINE), "active",
G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL));
- rb (e_binding_bind_property_full (
- cnt_editor, "font-name",
- editor->priv->font_name_combo_box, "active-id",
- G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
- e_html_editor_content_editor_font_name_to_combo_box,
- NULL,
- NULL, NULL));
/* Cannot use binding, due to subscript and superscript being mutually exclusive */
editor->priv->subscript_notify_id = g_signal_connect_object (cnt_editor, "notify::subscript",
@@ -2616,8 +2761,8 @@ e_html_editor_actions_update_spellcheck_languages_menu (EHTMLEditor *editor,
const gchar * const *languages)
{
GHashTable *active;
- GList *actions, *link;
- gint ii;
+ GPtrArray *actions;
+ guint ii;
g_return_if_fail (E_IS_HTML_EDITOR (editor));
@@ -2627,28 +2772,20 @@ e_html_editor_actions_update_spellcheck_languages_menu (EHTMLEditor *editor,
g_hash_table_insert (active, g_strdup (languages[ii]), NULL);
}
- actions = gtk_action_group_list_actions (editor->priv->language_actions);
- for (link = actions; link; link = g_list_next (link)) {
- GtkToggleAction *toggle_action;
+ actions = e_ui_action_group_list_actions (editor->priv->language_actions);
+ for (ii = 0; actions && ii < actions->len; ii++) {
+ EUIAction *action = g_ptr_array_index (actions, ii);
gboolean is_active;
- if (!GTK_IS_TOGGLE_ACTION (link->data))
- continue;
+ is_active = g_hash_table_contains (active, g_action_get_name (G_ACTION (action)));
- if (gtk_action_get_name (link->data) &&
- g_str_has_prefix (gtk_action_get_name (link->data), "recent-spell-language-"))
- continue;
-
- is_active = g_hash_table_contains (active, gtk_action_get_name (link->data));
- toggle_action = GTK_TOGGLE_ACTION (link->data);
-
- if ((gtk_toggle_action_get_active (toggle_action) ? 1 : 0) != (is_active ? 1 : 0)) {
- g_signal_handlers_block_by_func (toggle_action, action_language_cb, editor);
- gtk_toggle_action_set_active (toggle_action, is_active);
- g_signal_handlers_unblock_by_func (toggle_action, action_language_cb, editor);
+ if ((e_ui_action_get_active (action) ? 1 : 0) != (is_active ? 1 : 0)) {
+ g_signal_handlers_block_by_func (action, action_language_notify_active_cb, editor);
+ e_ui_action_set_active (action, is_active);
+ g_signal_handlers_unblock_by_func (action, action_language_notify_active_cb, editor);
}
}
+ g_clear_pointer (&actions, g_ptr_array_unref);
g_hash_table_destroy (active);
- g_list_free (actions);
}
diff --git a/src/e-util/e-html-editor-actions.h b/src/e-util/e-html-editor-actions.h
index 0adde4c3d9..2cca349077 100644
--- a/src/e-util/e-html-editor-actions.h
+++ b/src/e-util/e-html-editor-actions.h
@@ -74,7 +74,7 @@
#define E_HTML_EDITOR_ACTION_CONTEXT_SPELL_ADD(editor) \
E_HTML_EDITOR_ACTION ((editor), "context-spell-add")
#define E_HTML_EDITOR_ACTION_CONTEXT_SPELL_ADD_MENU(editor) \
- E_HTML_EDITOR_ACTION ((editor), "context-spell-add-menu")
+ E_HTML_EDITOR_ACTION ((editor), "EHTMLEditor::context-spell-add-menu")
#define E_HTML_EDITOR_ACTION_CONTEXT_SPELL_IGNORE(editor) \
E_HTML_EDITOR_ACTION ((editor), "context-spell-ignore")
#define E_HTML_EDITOR_ACTION_COPY(editor) \
diff --git a/src/e-util/e-html-editor-manager.ui b/src/e-util/e-html-editor-manager.ui
deleted file mode 100644
index 4592ea63a8..0000000000
--- a/src/e-util/e-html-editor-manager.ui
+++ /dev/null
@@ -1,196 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/e-util/e-html-editor-paragraph-dialog.c b/src/e-util/e-html-editor-paragraph-dialog.c
index 56c6a27067..d5748d1f1b 100644
--- a/src/e-util/e-html-editor-paragraph-dialog.c
+++ b/src/e-util/e-html-editor-paragraph-dialog.c
@@ -51,6 +51,8 @@ html_editor_paragraph_dialog_constructed (GObject *object)
dialog = E_HTML_EDITOR_PARAGRAPH_DIALOG (object);
editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+ e_ui_manager_add_action_groups_to_widget (e_html_editor_get_ui_manager (editor), GTK_WIDGET (dialog));
+
main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
/* == General == */
@@ -66,8 +68,7 @@ html_editor_paragraph_dialog_constructed (GObject *object)
gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
/* Style */
- widget = e_action_combo_box_new_with_action (
- GTK_RADIO_ACTION (e_html_editor_get_action (editor, "style-normal")));
+ widget = e_action_combo_box_new_with_action (e_html_editor_get_action (editor, "style-normal"));
gtk_widget_set_hexpand (widget, TRUE);
gtk_grid_attach (grid, widget, 1, 0, 1, 1);
dialog->priv->style_combo = widget;
@@ -92,9 +93,7 @@ html_editor_paragraph_dialog_constructed (GObject *object)
/* Left */
widget = gtk_toggle_button_new_with_label (_("_Left"));
gtk_button_set_use_stock (GTK_BUTTON (widget), TRUE);
- gtk_activatable_set_related_action (
- GTK_ACTIVATABLE (widget),
- e_html_editor_get_action (editor, "justify-left"));
+ e_ui_action_util_assign_to_widget (e_html_editor_get_action (editor, "justify-left"), widget);
gtk_grid_attach (grid, widget, 0, 0, 1, 1);
dialog->priv->left_button = widget;
@@ -102,27 +101,21 @@ html_editor_paragraph_dialog_constructed (GObject *object)
widget = gtk_toggle_button_new_with_label (_("_Center"));
gtk_button_set_use_stock (GTK_BUTTON (widget), TRUE);
gtk_grid_attach (grid, widget, 1, 0, 1, 1);
- gtk_activatable_set_related_action (
- GTK_ACTIVATABLE (widget),
- e_html_editor_get_action (editor, "justify-center"));
+ e_ui_action_util_assign_to_widget (e_html_editor_get_action (editor, "justify-center"), widget);
dialog->priv->center_button = widget;
/* Right */
widget = gtk_toggle_button_new_with_label (_("_Right"));
gtk_button_set_use_stock (GTK_BUTTON (widget), TRUE);
gtk_grid_attach (grid, widget, 2, 0, 1, 1);
- gtk_activatable_set_related_action (
- GTK_ACTIVATABLE (widget),
- e_html_editor_get_action (editor, "justify-right"));
+ e_ui_action_util_assign_to_widget (e_html_editor_get_action (editor, "justify-right"), widget);
dialog->priv->right_button = widget;
/* Justified */
widget = gtk_toggle_button_new_with_label (_("_Justified"));
gtk_button_set_use_stock (GTK_BUTTON (widget), TRUE);
gtk_grid_attach (grid, widget, 2, 0, 1, 1);
- gtk_activatable_set_related_action (
- GTK_ACTIVATABLE (widget),
- e_html_editor_get_action (editor, "justify-fill"));
+ e_ui_action_util_assign_to_widget (e_html_editor_get_action (editor, "justify-fill"), widget);
dialog->priv->justified_button = widget;
gtk_widget_show_all (GTK_WIDGET (main_layout));
diff --git a/src/e-util/e-html-editor-private.h b/src/e-util/e-html-editor-private.h
index d21029db09..d32baa103e 100644
--- a/src/e-util/e-html-editor-private.h
+++ b/src/e-util/e-html-editor-private.h
@@ -36,33 +36,48 @@
#include
#include
#include
+#include
+#include
#include
-#ifdef HAVE_XFREE
-#include
-#endif
-
#define ACTION(name) (E_HTML_EDITOR_ACTION_##name (editor))
-#define WIDGET(name) (E_HTML_EDITOR_WIDGETS_##name (editor))
G_BEGIN_DECLS
+typedef struct _EHTMLEditorActionMenuPair {
+ EUIAction *parent_menu_action; /* to control visibility of the parent menu item (submenu) */
+ GMenu *submenu_items; /* actual items of the submenu */
+} EHTMLEditorActionMenuPair;
+
+EHTMLEditorActionMenuPair *
+ e_html_editor_action_menu_pair_new (EUIAction *action, /* (transfer full) */
+ GMenu *menu); /* (transfer full) */
+void e_html_editor_action_menu_pair_free (gpointer ptr);
+
struct _EHTMLEditorPrivate {
EContentEditorMode mode;
GtkWidget *content_editors_box;
- GtkUIManager *manager;
- GtkActionGroup *core_actions;
- GtkActionGroup *core_editor_actions;
- GtkActionGroup *html_actions;
- GtkActionGroup *context_actions;
- GtkActionGroup *html_context_actions;
- GtkActionGroup *language_actions;
- GtkActionGroup *spell_check_actions;
- GtkActionGroup *suggestion_actions;
+ EUIManager *ui_manager;
+ EUIActionGroup *core_actions; /* owned by priv->manager */
+ EUIActionGroup *core_editor_actions; /* owned by priv->manager */
+ EUIActionGroup *html_actions; /* owned by priv->manager */
+ EUIActionGroup *context_actions; /* owned by priv->manager */
+ EUIActionGroup *html_context_actions; /* owned by priv->manager */
+ EUIActionGroup *language_actions; /* owned by priv->manager */
+ EUIActionGroup *spell_check_actions; /* owned by priv->manager */
+ EUIActionGroup *suggestion_actions; /* owned by priv->manager */
- GtkWidget *main_menu;
+ GMenu *emoticon_menu;
+ GMenu *recent_languages_menu;
+ GMenu *all_languages_menu;
+ GPtrArray *spell_suggest_actions; /* EUIAction; to fill a GMenu */
+ GPtrArray *spell_suggest_more_actions; /* EUIAction; to fill a GMenu */
+ GPtrArray *spell_add_actions; /* EUIAction, to fill a GMenu */
+ GHashTable *spell_suggest_menus_by_code; /* gchar *dictionary-code ~> EHTMLEditorActionMenuPair * */
+
+ EUIMenu *main_menu;
GtkWidget *main_toolbar;
GtkWidget *edit_toolbar;
GtkWidget *html_toolbar;
@@ -83,16 +98,10 @@ struct _EHTMLEditorPrivate {
GtkWidget *cell_dialog;
GtkWidget *spell_check_dialog;
- GtkWidget *fg_color_combo_box;
- GtkWidget *bg_color_combo_box;
- GtkWidget *mode_combo_box;
- GtkToolItem *mode_tool_item;
- GtkWidget *size_combo_box;
- GtkWidget *style_combo_box;
- GtkWidget *font_name_combo_box;
GtkWidget *scrolled_window;
GtkWidget *emoji_chooser;
+ GtkWidget *emoticon_chooser;
GHashTable *cid_parts; /* gchar *cid: URI ~> CamelMimePart * */
GHashTable *content_editors; /* gchar *name ~> EContentEditor * */
@@ -105,9 +114,6 @@ struct _EHTMLEditorPrivate {
gulong subscript_notify_id;
gulong superscript_notify_id;
- guint spell_suggestions_merge_id;
- guint recent_spell_languages_merge_id;
-
gint editor_layout_row;
gboolean paste_plain_prefer_pre;
@@ -115,7 +121,10 @@ struct _EHTMLEditorPrivate {
gchar *context_hover_uri;
};
-void e_html_editor_actions_init (EHTMLEditor *editor);
+void e_html_editor_actions_add_actions
+ (EHTMLEditor *editor);
+void e_html_editor_actions_setup_actions
+ (EHTMLEditor *editor);
void e_html_editor_actions_bind (EHTMLEditor *editor);
void e_html_editor_actions_unbind (EHTMLEditor *editor);
void e_html_editor_actions_update_spellcheck_languages_menu
@@ -130,6 +139,8 @@ gchar * e_html_editor_util_dup_font_id (GtkComboBox *combo_box,
gboolean e_html_editor_has_editor_for_mode
(EHTMLEditor *editor,
EContentEditorMode mode);
+void e_html_editor_emit_after_mode_changed
+ (EHTMLEditor *self);
G_END_DECLS
diff --git a/src/e-util/e-html-editor.c b/src/e-util/e-html-editor.c
index ad9c7da7e8..02112b6cd9 100644
--- a/src/e-util/e-html-editor.c
+++ b/src/e-util/e-html-editor.c
@@ -78,6 +78,7 @@ enum {
enum {
UPDATE_ACTIONS,
SPELL_LANGUAGES_CHANGED,
+ AFTER_MODE_CHANGED,
LAST_SIGNAL
};
@@ -220,20 +221,57 @@ e_html_editor_util_dup_font_id (GtkComboBox *combo_box,
return id;
}
+EHTMLEditorActionMenuPair *
+e_html_editor_action_menu_pair_new (EUIAction *action, /* (transfer full) */
+ GMenu *menu) /* (transfer full) */
+{
+ EHTMLEditorActionMenuPair *data;
+
+ data = g_new0 (EHTMLEditorActionMenuPair, 1);
+ data->parent_menu_action = action;
+ data->submenu_items = menu;
+
+ return data;
+}
+
+void
+e_html_editor_action_menu_pair_free (gpointer ptr)
+{
+ EHTMLEditorActionMenuPair *data = ptr;
+
+ if (data) {
+ g_clear_object (&data->parent_menu_action);
+ g_clear_object (&data->submenu_items);
+ g_free (data);
+ }
+}
+
+void
+e_html_editor_emit_after_mode_changed (EHTMLEditor *self)
+{
+ g_return_if_fail (E_IS_HTML_EDITOR (self));
+
+ g_signal_emit (self, signals[AFTER_MODE_CHANGED], 0, NULL);
+}
+
/* Action callback for context menu spelling suggestions.
* XXX This should really be in e-html-editor-actions.c */
static void
-action_context_spell_suggest_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_context_spell_suggest_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
- const gchar *word;
+ GVariant *state;
- word = g_object_get_data (G_OBJECT (action), "word");
- g_return_if_fail (word != NULL);
+ state = g_action_get_state (G_ACTION (action));
+ g_return_if_fail (state != NULL);
cnt_editor = e_html_editor_get_content_editor (editor);
- e_content_editor_replace_caret_word (cnt_editor, word);
+ e_content_editor_replace_caret_word (cnt_editor, g_variant_get_string (state, NULL));
+
+ g_clear_pointer (&state, g_variant_unref);
}
static void
@@ -242,27 +280,34 @@ html_editor_inline_spelling_suggestions (EHTMLEditor *editor,
{
EContentEditor *cnt_editor;
ESpellChecker *spell_checker;
- GtkActionGroup *action_group;
- GtkUIManager *manager;
+ EUIActionGroup *action_group;
+ GPtrArray *fill_actions;
gchar **suggestions;
- const gchar *path;
+ const gchar *map_name;
guint count = 0;
guint length;
- guint merge_id;
guint threshold;
gint ii;
+ gboolean had_any_before;
+
+ had_any_before = editor->priv->spell_suggest_actions->len > 0 || editor->priv->spell_suggest_more_actions->len > 0;
+
+ g_ptr_array_set_size (editor->priv->spell_suggest_actions, 0);
+ g_ptr_array_set_size (editor->priv->spell_suggest_more_actions, 0);
+
+ if (!caret_word || !*caret_word) {
+ if (had_any_before)
+ e_ui_menu_rebuild (editor->priv->main_menu);
+ return;
+ }
cnt_editor = e_html_editor_get_content_editor (editor);
- if (!caret_word || !*caret_word)
- return;
-
spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
suggestions = e_spell_checker_get_guesses_for_word (spell_checker, caret_word);
-
- path = "/context-menu/context-spell-suggest/";
- manager = e_html_editor_get_ui_manager (editor);
action_group = editor->priv->suggestion_actions;
- merge_id = editor->priv->spell_suggestions_merge_id;
+ map_name = e_ui_action_group_get_name (action_group);
+
+ fill_actions = editor->priv->spell_suggest_actions;
length = (suggestions != NULL) ? g_strv_length (suggestions) : 0;
@@ -278,55 +323,34 @@ html_editor_inline_spelling_suggestions (EHTMLEditor *editor,
for (ii = 0; suggestions && suggestions[ii]; ii++) {
gchar *suggestion = suggestions[ii];
- gchar *action_name;
- gchar *action_label;
- GtkAction *action;
- GtkWidget *child;
- GSList *proxies;
+ gchar action_name[128];
+ EUIAction *action;
/* Once we reach the threshold, put all subsequent
* spelling suggestions in a secondary menu. */
if (count == threshold)
- path = "/context-menu/context-more-suggestions-menu/";
+ fill_actions = editor->priv->spell_suggest_more_actions;
/* Action name just needs to be unique. */
- action_name = g_strdup_printf ("suggest-%d", count++);
- action_label = g_markup_printf_escaped ("%s", suggestion);
+ g_warn_if_fail (g_snprintf (action_name, sizeof (action_name), "suggest-%d", count++) < sizeof (action_name));
- action = gtk_action_new (action_name, action_label, NULL, NULL);
-
- g_object_set_data_full (
- G_OBJECT (action), "word",
- g_strdup (suggestion), g_free);
+ action = e_ui_action_new_stateful (map_name, action_name, NULL, g_variant_new_string (suggestion));
+ e_ui_action_set_label (action, suggestion);
g_signal_connect (
action, "activate",
G_CALLBACK (action_context_spell_suggest_cb), editor);
- gtk_action_group_add_action (action_group, action);
-
- gtk_ui_manager_add_ui (
- manager, merge_id, path,
- action_name, action_name,
- GTK_UI_MANAGER_AUTO, FALSE);
-
- /* XXX GtkAction offers no support for Pango markup,
- * so we have to manually set "use-markup" on the
- * child of the proxy widget. */
- gtk_ui_manager_ensure_update (manager);
- proxies = gtk_action_get_proxies (action);
-
- if (proxies) {
- child = gtk_bin_get_child (proxies->data);
- g_object_set (child, "use-markup", TRUE, NULL);
- }
-
- g_free (action_name);
- g_free (action_label);
+ e_ui_action_group_add (action_group, action);
+ /* the array assumes ownership of the action */
+ g_ptr_array_add (fill_actions, action);
}
g_strfreev (suggestions);
g_clear_object (&spell_checker);
+
+ if (had_any_before || count > 0)
+ e_ui_menu_rebuild (editor->priv->main_menu);
}
/* Helper for html_editor_update_actions() */
@@ -338,17 +362,23 @@ html_editor_spell_checkers_foreach (EHTMLEditor *editor,
EContentEditor *cnt_editor;
ESpellChecker *spell_checker;
ESpellDictionary *dictionary = NULL;
- GtkActionGroup *action_group;
- GtkUIManager *manager;
+ EUIActionGroup *action_group;
+ EHTMLEditorActionMenuPair *action_menu_pair;
GList *list, *link;
- gchar *path;
+ const gchar *map_name;
gint ii = 0;
- guint merge_id;
+
+ action_menu_pair = g_hash_table_lookup (editor->priv->spell_suggest_menus_by_code, language_code);
+ g_return_if_fail (action_menu_pair != NULL);
+
+ g_menu_remove_all (action_menu_pair->submenu_items);
+
+ if (!caret_word || !*caret_word) {
+ e_ui_action_set_visible (action_menu_pair->parent_menu_action, FALSE);
+ return;
+ }
cnt_editor = e_html_editor_get_content_editor (editor);
- if (!caret_word || !*caret_word)
- return;
-
spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
dictionary = e_spell_checker_ref_dictionary (spell_checker, language_code);
@@ -359,60 +389,43 @@ html_editor_spell_checkers_foreach (EHTMLEditor *editor,
list = NULL;
}
- manager = e_html_editor_get_ui_manager (editor);
action_group = editor->priv->suggestion_actions;
- merge_id = editor->priv->spell_suggestions_merge_id;
-
- path = g_strdup_printf (
- "/context-menu/context-spell-suggest/"
- "context-spell-suggest-%s-menu", language_code);
+ map_name = e_ui_action_group_get_name (action_group);
+ e_ui_action_set_visible (action_menu_pair->parent_menu_action, list != NULL);
for (link = list; link != NULL; link = g_list_next (link), ii++) {
gchar *suggestion = link->data;
- gchar *action_name;
- gchar *action_label;
- GtkAction *action;
- GtkWidget *child;
- GSList *proxies;
+ gchar action_name[128];
+ EUIAction *action;
+ GMenuItem *menu_item;
/* Action name just needs to be unique. */
- action_name = g_strdup_printf ("suggest-%s-%d", language_code, ii);
- action_label = g_markup_printf_escaped ("%s", suggestion);
+ g_snprintf (action_name, sizeof (action_name), "suggest-%s-%d", language_code, ii);
- action = gtk_action_new (action_name, action_label, NULL, NULL);
-
- g_object_set_data_full (
- G_OBJECT (action), "word",
- g_strdup (suggestion), g_free);
+ action = e_ui_action_new_stateful (map_name, action_name, NULL, g_variant_new_string (suggestion));
+ e_ui_action_set_label (action, suggestion);
g_signal_connect (
action, "activate",
G_CALLBACK (action_context_spell_suggest_cb), editor);
- gtk_action_group_add_action (action_group, action);
+ e_ui_action_group_add (action_group, action);
- gtk_ui_manager_add_ui (
- manager, merge_id, path,
- action_name, action_name,
- GTK_UI_MANAGER_AUTO, FALSE);
+ menu_item = g_menu_item_new (NULL, NULL);
+ e_ui_manager_update_item_from_action (editor->priv->ui_manager, menu_item, action);
+ /* to not look like a radio item */
+ g_menu_item_set_attribute (menu_item, G_MENU_ATTRIBUTE_TARGET, NULL);
- /* XXX GtkAction offers no supports for Pango markup,
- * so we have to manually set "use-markup" on the
- * child of the proxy widget. */
- gtk_ui_manager_ensure_update (manager);
- proxies = gtk_action_get_proxies (action);
- if (proxies && proxies->data) {
- child = gtk_bin_get_child (proxies->data);
- g_object_set (child, "use-markup", TRUE, NULL);
- }
+ g_menu_append_item (action_menu_pair->submenu_items, menu_item);
- g_free (action_name);
- g_free (action_label);
+ g_clear_object (&menu_item);
+
+ if (link == list)
+ g_ptr_array_add (editor->priv->spell_suggest_actions, g_object_ref (action_menu_pair->parent_menu_action));
}
g_list_free_full (list, (GDestroyNotify) g_free);
g_clear_object (&spell_checker);
- g_free (path);
}
void
@@ -427,22 +440,22 @@ e_html_editor_update_spell_actions (EHTMLEditor *editor)
count = e_spell_checker_count_active_languages (spell_checker);
- gtk_action_set_visible (ACTION (CONTEXT_SPELL_ADD), count == 1);
- gtk_action_set_visible (ACTION (CONTEXT_SPELL_ADD_MENU), count > 1);
- gtk_action_set_visible (ACTION (CONTEXT_SPELL_IGNORE), count > 0);
+ e_ui_action_set_visible (ACTION (CONTEXT_SPELL_ADD), count == 1);
+ e_ui_action_set_visible (ACTION (CONTEXT_SPELL_ADD_MENU), count > 1);
+ e_ui_action_set_visible (ACTION (CONTEXT_SPELL_IGNORE), count > 0);
- gtk_action_set_sensitive (ACTION (SPELL_CHECK), count > 0);
- gtk_action_set_sensitive (ACTION (LANGUAGE_MENU), e_spell_checker_count_available_dicts (spell_checker) > 0);
+ e_ui_action_set_sensitive (ACTION (SPELL_CHECK), count > 0);
+ e_ui_action_set_sensitive (ACTION (LANGUAGE_MENU), e_spell_checker_count_available_dicts (spell_checker) > 0);
g_clear_object (&spell_checker);
}
static void
-action_set_visible_and_sensitive (GtkAction *action,
+action_set_visible_and_sensitive (EUIAction *action,
gboolean value)
{
- gtk_action_set_visible (action, value);
- gtk_action_set_sensitive (action, value);
+ e_ui_action_set_visible (action, value);
+ e_ui_action_set_sensitive (action, value);
}
static void
@@ -453,13 +466,11 @@ html_editor_update_actions (EHTMLEditor *editor,
{
EContentEditor *cnt_editor;
ESpellChecker *spell_checker;
- GtkUIManager *manager;
- GtkActionGroup *action_group;
- GList *list;
+ GHashTableIter iter;
+ gpointer value = NULL;
gchar **languages = NULL;
guint ii, n_languages;
gboolean visible;
- guint merge_id;
cnt_editor = e_html_editor_get_content_editor (editor);
@@ -492,8 +503,8 @@ html_editor_update_actions (EHTMLEditor *editor,
visible && !(flags & E_CONTENT_EDITOR_NODE_IS_TEXT_COLLAPSED));
visible =
- gtk_action_get_visible (ACTION (CONTEXT_PROPERTIES_IMAGE)) ||
- gtk_action_get_visible (ACTION (CONTEXT_PROPERTIES_LINK)) ||
+ e_ui_action_get_visible (ACTION (CONTEXT_PROPERTIES_IMAGE)) ||
+ e_ui_action_get_visible (ACTION (CONTEXT_PROPERTIES_LINK)) ||
visible; /* text node under caret */
action_set_visible_and_sensitive (ACTION (CONTEXT_PROPERTIES_PARAGRAPH), visible);
@@ -521,24 +532,16 @@ html_editor_update_actions (EHTMLEditor *editor,
/********************** Spell Check Suggestions **********************/
- manager = e_html_editor_get_ui_manager (editor);
- action_group = editor->priv->suggestion_actions;
-
/* Remove the old content from the context menu. */
- merge_id = editor->priv->spell_suggestions_merge_id;
- if (merge_id > 0) {
- gtk_ui_manager_remove_ui (manager, merge_id);
- editor->priv->spell_suggestions_merge_id = 0;
+ g_hash_table_iter_init (&iter, editor->priv->spell_suggest_menus_by_code);
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
+ EHTMLEditorActionMenuPair *action_menu_pair = value;
+ e_ui_action_set_visible (action_menu_pair->parent_menu_action, FALSE);
+ g_menu_remove_all (action_menu_pair->submenu_items);
}
/* Clear the action group for spelling suggestions. */
- list = gtk_action_group_list_actions (action_group);
- while (list != NULL) {
- GtkAction *action = list->data;
-
- gtk_action_group_remove_action (action_group, action);
- list = g_list_delete_link (list, list);
- }
+ e_ui_action_group_remove_all (editor->priv->suggestion_actions);
spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
languages = e_spell_checker_list_active_languages (
@@ -554,8 +557,7 @@ html_editor_update_actions (EHTMLEditor *editor,
}
}
- action_group = editor->priv->spell_check_actions;
- gtk_action_group_set_visible (action_group, visible);
+ e_ui_action_group_set_visible (editor->priv->spell_check_actions, visible);
g_clear_object (&spell_checker);
@@ -565,9 +567,6 @@ html_editor_update_actions (EHTMLEditor *editor,
return;
}
- merge_id = gtk_ui_manager_new_merge_id (manager);
- editor->priv->spell_suggestions_merge_id = merge_id;
-
/* Handle a single active language as a special case. */
if (n_languages == 1) {
html_editor_inline_spelling_suggestions (editor, caret_word);
@@ -577,6 +576,9 @@ html_editor_update_actions (EHTMLEditor *editor,
return;
}
+ g_ptr_array_set_size (editor->priv->spell_suggest_actions, 0);
+ g_ptr_array_set_size (editor->priv->spell_suggest_more_actions, 0);
+
/* Add actions and context menu content for active languages. */
for (ii = 0; ii < n_languages; ii++)
html_editor_spell_checkers_foreach (editor, languages[ii], caret_word);
@@ -643,16 +645,6 @@ context_menu_data_free (gpointer ptr)
}
}
-static void
-html_editor_menu_deactivate_cb (GtkMenu *popup_menu,
- gpointer user_data)
-{
- g_return_if_fail (GTK_IS_MENU (popup_menu));
-
- g_signal_handlers_disconnect_by_func (popup_menu, html_editor_menu_deactivate_cb, user_data);
- gtk_menu_detach (popup_menu);
-}
-
static gboolean
html_editor_show_context_menu_idle_cb (gpointer user_data)
{
@@ -663,21 +655,22 @@ html_editor_show_context_menu_idle_cb (gpointer user_data)
editor = g_weak_ref_get (cmd->editor_weakref);
if (editor) {
- GtkWidget *menu;
-
- menu = e_html_editor_get_managed_widget (editor, "/context-menu");
+ GtkMenu *menu;
+ GObject *ui_item;
+ e_ui_menu_freeze (editor->priv->main_menu);
g_signal_emit (editor, signals[UPDATE_ACTIONS], 0, cmd->flags, cmd->caret_word, cmd->hover_uri);
+ e_ui_menu_thaw (editor->priv->main_menu);
- if (!gtk_menu_get_attach_widget (GTK_MENU (menu))) {
- gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (editor), NULL);
+ ui_item = e_ui_manager_create_item (editor->priv->ui_manager, "context-menu");
+ menu = GTK_MENU (gtk_menu_new_from_model (G_MENU_MODEL (ui_item)));
+ g_clear_object (&ui_item);
- g_signal_connect (
- menu, "deactivate",
- G_CALLBACK (html_editor_menu_deactivate_cb), NULL);
- }
+ gtk_menu_attach_to_widget (menu, GTK_WIDGET (editor), NULL);
- gtk_menu_popup_at_pointer (GTK_MENU (menu), cmd->event);
+ e_util_connect_menu_detach_after_deactivate (menu);
+
+ gtk_menu_popup_at_pointer (menu, cmd->event);
g_object_unref (editor);
}
@@ -715,12 +708,16 @@ html_editor_find_ui_file (const gchar *basename)
g_return_val_if_fail (basename != NULL, NULL);
/* Support running directly from the source tree. */
- filename = g_build_filename (".", basename, NULL);
+ filename = g_build_filename (".", "data", "ui", basename, NULL);
+ if (g_file_test (filename, G_FILE_TEST_EXISTS))
+ return filename;
+ g_free (filename);
+
+ filename = g_build_filename ("..", "..", "..", "data", "ui", basename, NULL);
if (g_file_test (filename, G_FILE_TEST_EXISTS))
return filename;
g_free (filename);
- /* XXX This is kinda broken. */
filename = g_build_filename (EVOLUTION_UIDIR, basename, NULL);
if (g_file_test (filename, G_FILE_TEST_EXISTS))
return filename;
@@ -743,7 +740,8 @@ html_editor_parent_changed (GtkWidget *widget,
if (GTK_IS_WINDOW (top_level)) {
gtk_window_add_accel_group (
GTK_WINDOW (top_level),
- gtk_ui_manager_get_accel_group (editor->priv->manager));
+ e_ui_manager_get_accel_group (editor->priv->ui_manager));
+ e_ui_manager_set_action_groups_widget (editor->priv->ui_manager, top_level);
}
}
@@ -811,6 +809,367 @@ e_html_editor_edit_html_toolbar_visible_cb (GBinding *binding,
return TRUE;
}
+static gboolean
+e_html_editor_content_editor_font_name_to_combo_box (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ gchar *id = NULL;
+
+ id = e_html_editor_util_dup_font_id (GTK_COMBO_BOX (g_binding_get_target (binding)), g_value_get_string (from_value));
+ g_value_take_string (to_value, id ? id : g_strdup (""));
+
+ return TRUE;
+}
+
+static void
+e_html_editor_unbind_and_unref (gpointer ptr)
+{
+ GBinding *binding = ptr;
+
+ if (binding) {
+ g_binding_unbind (binding);
+ g_object_unref (binding);
+ }
+}
+
+static void
+e_html_editor_bind_font_name_after_mode_changed_cb (EHTMLEditor *self,
+ gpointer user_data)
+{
+ GtkWidget *widget = user_data;
+ EContentEditor *cnt_editor;
+
+ g_return_if_fail (E_IS_HTML_EDITOR (self));
+ g_return_if_fail (GTK_IS_COMBO_BOX (widget));
+
+ cnt_editor = e_html_editor_get_content_editor (self);
+ if (cnt_editor) {
+ GBinding *binding;
+
+ binding = e_binding_bind_property_full (
+ cnt_editor, "font-name",
+ widget, "active-id",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
+ e_html_editor_content_editor_font_name_to_combo_box,
+ NULL,
+ NULL, NULL);
+ g_object_set_data_full (G_OBJECT (widget), "EHTMLEditor::binding", g_object_ref (binding),
+ e_html_editor_unbind_and_unref);
+
+ binding = e_binding_bind_property (
+ cnt_editor, "editable",
+ widget, "sensitive",
+ G_BINDING_SYNC_CREATE);
+ g_object_set_data_full (G_OBJECT (widget), "EHTMLEditor::binding-sensitive", g_object_ref (binding),
+ e_html_editor_unbind_and_unref);
+
+ gtk_widget_set_sensitive (widget, e_content_editor_is_editable (cnt_editor) && e_html_editor_get_mode (self) == E_CONTENT_EDITOR_MODE_HTML);
+ } else {
+ g_object_set_data (G_OBJECT (widget), "EHTMLEditor::binding", NULL);
+ g_object_set_data (G_OBJECT (widget), "EHTMLEditor::binding-sensitive", NULL);
+ }
+}
+
+static void
+e_html_editor_bind_color_combox_box (EHTMLEditor *self,
+ GtkWidget *widget,
+ const gchar *property_name)
+{
+ EContentEditor *cnt_editor;
+
+ cnt_editor = e_html_editor_get_content_editor (self);
+ if (cnt_editor) {
+ GBinding *binding;
+
+ binding = e_binding_bind_property (
+ widget, "current-color",
+ cnt_editor, property_name,
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ g_object_set_data_full (G_OBJECT (widget), "EHTMLEditor::binding", g_object_ref (binding),
+ e_html_editor_unbind_and_unref);
+
+ binding = e_binding_bind_property (
+ cnt_editor, "editable",
+ widget, "sensitive",
+ G_BINDING_SYNC_CREATE);
+ g_object_set_data_full (G_OBJECT (widget), "EHTMLEditor::binding-sensitive", g_object_ref (binding),
+ e_html_editor_unbind_and_unref);
+
+ gtk_widget_set_sensitive (widget, e_content_editor_is_editable (cnt_editor) && e_html_editor_get_mode (self) == E_CONTENT_EDITOR_MODE_HTML);
+ } else {
+ g_object_set_data (G_OBJECT (widget), "EHTMLEditor::binding", NULL);
+ g_object_set_data (G_OBJECT (widget), "EHTMLEditor::binding-sensitive", NULL);
+ gtk_widget_set_sensitive (widget, FALSE);
+ }
+}
+
+static void
+e_html_editor_bind_font_color_after_mode_changed_cb (EHTMLEditor *self,
+ gpointer user_data)
+{
+ GtkWidget *widget = user_data;
+
+ g_return_if_fail (E_IS_HTML_EDITOR (self));
+ g_return_if_fail (E_IS_COLOR_COMBO (widget));
+
+ e_html_editor_bind_color_combox_box (self, widget, "font-color");
+}
+
+static void
+e_html_editor_bind_background_color_after_mode_changed_cb (EHTMLEditor *self,
+ gpointer user_data)
+{
+ GtkWidget *widget = user_data;
+
+ g_return_if_fail (E_IS_HTML_EDITOR (self));
+ g_return_if_fail (E_IS_COLOR_COMBO (widget));
+
+ e_html_editor_bind_color_combox_box (self, widget, "background-color");
+}
+
+static GObject *
+e_html_editor_create_submenu_from_actions (EHTMLEditor *self,
+ const gchar *label,
+ GPtrArray *actions) /* EUIAction * */
+{
+ GMenuItem *submenu;
+ GMenu *menu;
+ guint ii, n_added = 0;
+
+ menu = g_menu_new ();
+
+ for (ii = 0; ii < actions->len; ii++) {
+ EUIAction *subaction = g_ptr_array_index (actions, ii);
+ GMenuItem *item;
+
+ if (!e_ui_action_is_visible (subaction))
+ continue;
+
+ item = g_menu_item_new (NULL, NULL);
+ e_ui_manager_update_item_from_action (self->priv->ui_manager, item, subaction);
+ /* to not look like a radio item */
+ g_menu_item_set_attribute (item, G_MENU_ATTRIBUTE_TARGET, NULL);
+ g_menu_append_item (menu, item);
+ g_clear_object (&item);
+
+ n_added++;
+ }
+
+ if (n_added) {
+ if (label)
+ submenu = g_menu_item_new_submenu (label, G_MENU_MODEL (menu));
+ else
+ submenu = g_menu_item_new_section (NULL, G_MENU_MODEL (menu));
+ } else {
+ submenu = NULL;
+ }
+
+ g_clear_object (&menu);
+
+ return submenu ? G_OBJECT (submenu) : NULL;
+}
+
+static GObject *
+e_html_editor_create_spell_add_menu (EHTMLEditor *self,
+ EUIAction *action)
+{
+ return e_html_editor_create_submenu_from_actions (self, e_ui_action_get_label (action), self->priv->spell_add_actions);
+}
+
+static gboolean
+e_html_editor_spell_is_single_lang (EHTMLEditor *self)
+{
+ EContentEditor *cnt_editor;
+ ESpellChecker *spell_checker;
+ gboolean single_lang;
+
+ cnt_editor = e_html_editor_get_content_editor (self);
+ spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
+ single_lang = spell_checker && e_spell_checker_count_active_languages (spell_checker) == 1;
+ g_clear_object (&spell_checker);
+
+ return single_lang;
+}
+
+static GObject *
+e_html_editor_create_spell_suggest (EHTMLEditor *self,
+ EUIAction *action)
+{
+ GMenu *menu;
+ GMenuItem *item;
+ GHashTableIter iter;
+ GHashTable *submenu_by_action; /* EUIAction ~> GMenu */
+ gpointer value;
+ guint ii, n_added = 0;
+
+ if (e_html_editor_spell_is_single_lang (self))
+ return e_html_editor_create_submenu_from_actions (self, NULL, self->priv->spell_suggest_actions);
+
+ submenu_by_action = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+ g_hash_table_iter_init (&iter, self->priv->spell_suggest_menus_by_code);
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
+ EHTMLEditorActionMenuPair *pair = value;
+
+ if (e_ui_action_is_visible (pair->parent_menu_action))
+ g_hash_table_insert (submenu_by_action, pair->parent_menu_action, pair->submenu_items);
+ }
+
+ menu = g_menu_new ();
+
+ for (ii = 0; ii < self->priv->spell_suggest_actions->len; ii++) {
+ EUIAction *subaction = g_ptr_array_index (self->priv->spell_suggest_actions, ii);
+ GMenuModel *submenu;
+
+ if (!e_ui_action_is_visible (subaction))
+ continue;
+
+ submenu = g_hash_table_lookup (submenu_by_action, subaction);
+ if (!submenu) {
+ g_warn_if_reached ();
+ continue;
+ }
+
+ item = g_menu_item_new_submenu (e_ui_action_get_label (subaction), submenu);
+ g_menu_append_item (menu, item);
+ g_clear_object (&item);
+
+ n_added++;
+ }
+
+ g_hash_table_destroy (submenu_by_action);
+
+ if (n_added)
+ item = g_menu_item_new_section (NULL, G_MENU_MODEL (menu));
+ else
+ item = NULL;
+
+ g_clear_object (&menu);
+
+ return item ? G_OBJECT (item) : NULL;
+}
+
+static GObject *
+e_html_editor_create_spell_suggest_more_menu (EHTMLEditor *self,
+ EUIAction *action)
+{
+ if (!e_html_editor_spell_is_single_lang (self))
+ return NULL;
+
+ return e_html_editor_create_submenu_from_actions (self, e_ui_action_get_label (action), self->priv->spell_suggest_more_actions);
+}
+
+static gboolean
+e_html_editor_ui_manager_create_item_cb (EUIManager *ui_manager,
+ EUIElement *elem,
+ EUIAction *action,
+ EUIElementKind for_kind,
+ GObject **out_item,
+ gpointer user_data)
+{
+ EHTMLEditor *self = user_data;
+ const gchar *name;
+
+ g_return_val_if_fail (E_IS_HTML_EDITOR (self), FALSE);
+
+ name = g_action_get_name (G_ACTION (action));
+
+ if (!g_str_has_prefix (name, "EHTMLEditor::"))
+ return FALSE;
+
+ #define is_action(_nm) (g_strcmp0 (name, (_nm)) == 0)
+
+ if (for_kind == E_UI_ELEMENT_KIND_MENU) {
+ if (is_action ("EHTMLEditor::recent-languages"))
+ *out_item = G_OBJECT (g_menu_item_new_section (NULL, G_MENU_MODEL (self->priv->recent_languages_menu)));
+ else if (is_action ("EHTMLEditor::all-languages"))
+ *out_item = G_OBJECT (g_menu_item_new_section (NULL, G_MENU_MODEL (self->priv->all_languages_menu)));
+ else if (is_action ("EHTMLEditor::context-spell-suggest"))
+ *out_item = e_html_editor_create_spell_suggest (self, action);
+ else if (is_action ("EHTMLEditor::context-spell-suggest-more-menu"))
+ *out_item = e_html_editor_create_spell_suggest_more_menu (self, action);
+ else if (is_action ("EHTMLEditor::context-spell-add-menu"))
+ *out_item = e_html_editor_create_spell_add_menu (self, action);
+ else if (is_action ("EHTMLEditor::insert-emoticon"))
+ *out_item = G_OBJECT (g_menu_item_new_submenu (e_ui_action_get_label (action), G_MENU_MODEL (self->priv->emoticon_menu)));
+ else
+ g_warning ("%s: Unhandled menu action '%s'", G_STRFUNC, name);
+ } else if (for_kind == E_UI_ELEMENT_KIND_TOOLBAR) {
+ GtkWidget *widget = NULL;
+ gboolean claimed = FALSE;
+
+ if (is_action ("EHTMLEditor::editing-mode")) {
+ widget = e_action_combo_box_new_with_action (E_HTML_EDITOR_ACTION_MODE_HTML (self));
+ gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (widget), FALSE);
+ gtk_widget_set_tooltip_text (widget, _("Editing Mode"));
+ } else if (is_action ("EHTMLEditor::paragraph-style")) {
+ widget = e_action_combo_box_new_with_action (E_HTML_EDITOR_ACTION_STYLE_NORMAL (self));
+ gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (widget), FALSE);
+ gtk_widget_set_tooltip_text (widget, _("Paragraph Style"));
+
+ /* Hide the items from the action combo box as well */
+ g_signal_connect_object (self, "after-mode-changed",
+ G_CALLBACK (e_action_combo_box_update_model), widget, G_CONNECT_SWAPPED);
+ } else if (is_action ("EHTMLEditor::font-name")) {
+ widget = e_html_editor_util_create_font_name_combo ();
+ gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (widget), FALSE);
+ gtk_widget_set_tooltip_text (widget, _("Font Name"));
+
+ g_signal_connect_object (self, "after-mode-changed",
+ G_CALLBACK (e_html_editor_bind_font_name_after_mode_changed_cb), widget, 0);
+ e_html_editor_bind_font_name_after_mode_changed_cb (self, widget);
+ } else if (is_action ("EHTMLEditor::font-size")) {
+ widget = e_action_combo_box_new_with_action (E_HTML_EDITOR_ACTION_SIZE_PLUS_ZERO (self));
+ gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (widget), FALSE);
+ gtk_widget_set_tooltip_text (widget, _("Font Size"));
+ } else if (is_action ("EHTMLEditor::font-color")) {
+ widget = e_color_combo_new ();
+ gtk_widget_set_tooltip_text (widget, _("Font Color"));
+
+ g_signal_connect_object (self, "after-mode-changed",
+ G_CALLBACK (e_html_editor_bind_font_color_after_mode_changed_cb), widget, 0);
+ e_html_editor_bind_font_color_after_mode_changed_cb (self, widget);
+ } else if (is_action ("EHTMLEditor::background-color")) {
+ GdkRGBA transparent = { 0, 0, 0, 0 };
+
+ widget = e_color_combo_new ();
+ e_color_combo_set_default_color (E_COLOR_COMBO (widget), &transparent);
+ e_color_combo_set_current_color (E_COLOR_COMBO (widget), &transparent);
+ e_color_combo_set_default_transparent (E_COLOR_COMBO (widget), TRUE);
+ gtk_widget_set_tooltip_text (widget, _("Background Color"));
+
+ g_signal_connect_object (self, "after-mode-changed",
+ G_CALLBACK (e_html_editor_bind_background_color_after_mode_changed_cb), widget, 0);
+ e_html_editor_bind_background_color_after_mode_changed_cb (self, widget);
+ } else {
+ g_warning ("%s: Unhandled toolbar action '%s'", G_STRFUNC, name);
+ claimed = TRUE;
+ }
+
+ if (widget) {
+ GtkToolItem *tool_item;
+
+ tool_item = gtk_tool_item_new ();
+ gtk_container_add (GTK_CONTAINER (tool_item), widget);
+ gtk_widget_show_all (GTK_WIDGET (tool_item));
+
+ *out_item = G_OBJECT (tool_item);
+ } else if (!claimed) {
+ g_warning ("%s: Did not get toolbar widget for '%s'", G_STRFUNC, name);
+ }
+ } else if (for_kind == E_UI_ELEMENT_KIND_HEADERBAR) {
+ g_warning ("%s: Unhandled headerbar action '%s'", G_STRFUNC, name);
+ } else {
+ g_warning ("%s: Unhandled element kind '%d' for action '%s'", G_STRFUNC, (gint) for_kind, name);
+ }
+
+ #undef is_action
+
+ return TRUE;
+}
+
static void
html_editor_set_property (GObject *object,
guint property_id,
@@ -875,10 +1234,9 @@ html_editor_constructed (GObject *object)
EHTMLEditor *editor = E_HTML_EDITOR (object);
EHTMLEditorPrivate *priv = editor->priv;
GSettings *settings;
- GdkRGBA transparent = { 0, 0, 0, 0 };
GtkWidget *widget;
- GtkToolbar *toolbar;
- GtkToolItem *tool_item;
+ gchar *filename;
+ GError *local_error = NULL;
/* Chain up to parent's method. */
G_OBJECT_CLASS (e_html_editor_parent_class)->constructed (object);
@@ -901,29 +1259,41 @@ html_editor_constructed (GObject *object)
gtk_grid_attach (GTK_GRID (editor), widget, 0, 4, 1, 1);
priv->content_editors_box = widget;
- e_html_editor_actions_init (editor);
+ e_ui_manager_freeze (priv->ui_manager);
+
+ e_html_editor_actions_add_actions (editor);
priv->editor_layout_row = 2;
- /* Tweak the main-toolbar style. */
- widget = e_html_editor_get_managed_widget (editor, "/main-toolbar");
- gtk_style_context_add_class (
- gtk_widget_get_style_context (widget),
- GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
+ filename = html_editor_find_ui_file ("e-html-editor.eui");
+
+ if (!e_ui_parser_merge_file (e_ui_manager_get_parser (priv->ui_manager), filename, &local_error))
+ g_critical ("Couldn't load .eui file: %s", local_error ? local_error->message : "Unknown error");
+
+ g_free (filename);
+ g_clear_error (&local_error);
+
+ e_ui_manager_thaw (priv->ui_manager);
+
+ priv->main_menu = E_UI_MENU (e_ui_manager_create_item (editor->priv->ui_manager, "main-menu"));
+
+ if (e_util_get_use_header_bar ())
+ widget = GTK_WIDGET (e_ui_manager_create_item (editor->priv->ui_manager, "main-toolbar-with-headerbar"));
+ else
+ widget = GTK_WIDGET (e_ui_manager_create_item (editor->priv->ui_manager, "main-toolbar-without-headerbar"));
+ editor->priv->main_toolbar = g_object_ref_sink (widget);
/* Construct the editing toolbars. */
- widget = e_html_editor_get_managed_widget (editor, "/edit-toolbar");
+ widget = GTK_WIDGET (e_ui_manager_create_item (editor->priv->ui_manager, "edit-toolbar"));
gtk_widget_set_hexpand (widget, TRUE);
gtk_toolbar_set_style (GTK_TOOLBAR (widget), GTK_TOOLBAR_BOTH_HORIZ);
- e_util_setup_toolbar_icon_size (GTK_TOOLBAR (widget), GTK_ICON_SIZE_BUTTON);
gtk_grid_attach (GTK_GRID (editor), widget, 0, 0, 1, 1);
priv->edit_toolbar = g_object_ref (widget);
gtk_widget_show (widget);
- widget = e_html_editor_get_managed_widget (editor, "/html-toolbar");
+ widget = GTK_WIDGET (e_ui_manager_create_item (editor->priv->ui_manager, "html-toolbar"));
gtk_widget_set_hexpand (widget, TRUE);
gtk_toolbar_set_style (GTK_TOOLBAR (widget), GTK_TOOLBAR_BOTH_HORIZ);
- e_util_setup_toolbar_icon_size (GTK_TOOLBAR (widget), GTK_ICON_SIZE_BUTTON);
gtk_grid_attach (GTK_GRID (editor), widget, 0, 1, 1, 1);
priv->html_toolbar = g_object_ref (widget);
@@ -942,81 +1312,15 @@ html_editor_constructed (GObject *object)
priv->alert_bar = g_object_ref (widget);
/* EAlertBar controls its own visibility. */
+ e_ui_manager_freeze (priv->ui_manager);
+ /* do this only when the EUIMenu and others are available */
+ e_html_editor_actions_setup_actions (editor);
+ e_ui_manager_thaw (priv->ui_manager);
+
/* Have the default editor added (it's done inside the function) */
widget = GTK_WIDGET (e_html_editor_get_content_editor (editor));
gtk_widget_show (widget);
- /* Add some combo boxes to the "edit" toolbar. */
-
- toolbar = GTK_TOOLBAR (priv->edit_toolbar);
-
- tool_item = gtk_tool_item_new ();
- widget = e_action_combo_box_new_with_action (
- GTK_RADIO_ACTION (ACTION (STYLE_NORMAL)));
- gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (widget), FALSE);
- gtk_container_add (GTK_CONTAINER (tool_item), widget);
- gtk_widget_set_tooltip_text (widget, _("Paragraph Style"));
- gtk_toolbar_insert (toolbar, tool_item, 0);
- priv->style_combo_box = g_object_ref (widget);
- gtk_widget_show_all (GTK_WIDGET (tool_item));
-
- tool_item = gtk_separator_tool_item_new ();
- gtk_toolbar_insert (toolbar, tool_item, 0);
- gtk_widget_show_all (GTK_WIDGET (tool_item));
-
- tool_item = gtk_tool_item_new ();
- widget = e_action_combo_box_new_with_action (
- GTK_RADIO_ACTION (ACTION (MODE_HTML)));
- gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (widget), FALSE);
- gtk_container_add (GTK_CONTAINER (tool_item), widget);
- gtk_widget_set_tooltip_text (widget, _("Editing Mode"));
- gtk_toolbar_insert (toolbar, tool_item, 0);
- priv->mode_combo_box = g_object_ref (widget);
- priv->mode_tool_item = g_object_ref (tool_item);
- gtk_widget_show_all (GTK_WIDGET (tool_item));
-
- /* Add some combo boxes to the "html" toolbar. */
-
- toolbar = GTK_TOOLBAR (priv->html_toolbar);
-
- tool_item = gtk_tool_item_new ();
- widget = e_color_combo_new ();
- gtk_container_add (GTK_CONTAINER (tool_item), widget);
- gtk_widget_set_tooltip_text (widget, _("Font Color"));
- gtk_toolbar_insert (toolbar, tool_item, 0);
- priv->fg_color_combo_box = g_object_ref (widget);
- gtk_widget_show_all (GTK_WIDGET (tool_item));
-
- tool_item = gtk_tool_item_new ();
- widget = e_color_combo_new ();
- e_color_combo_set_default_color (E_COLOR_COMBO (widget), &transparent);
- e_color_combo_set_current_color (E_COLOR_COMBO (widget), &transparent);
- e_color_combo_set_default_transparent (E_COLOR_COMBO (widget), TRUE);
- gtk_container_add (GTK_CONTAINER (tool_item), widget);
- gtk_widget_set_tooltip_text (widget, _("Background Color"));
- gtk_toolbar_insert (toolbar, tool_item, 1);
- priv->bg_color_combo_box = g_object_ref (widget);
- gtk_widget_show_all (GTK_WIDGET (tool_item));
-
- tool_item = gtk_tool_item_new ();
- widget = e_action_combo_box_new_with_action (
- GTK_RADIO_ACTION (ACTION (SIZE_PLUS_ZERO)));
- gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (widget), FALSE);
- gtk_container_add (GTK_CONTAINER (tool_item), widget);
- gtk_widget_set_tooltip_text (widget, _("Font Size"));
- gtk_toolbar_insert (toolbar, tool_item, 0);
- priv->size_combo_box = g_object_ref (widget);
- gtk_widget_show_all (GTK_WIDGET (tool_item));
-
- tool_item = gtk_tool_item_new ();
- widget = e_html_editor_util_create_font_name_combo ();
- gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (widget), FALSE);
- gtk_container_add (GTK_CONTAINER (tool_item), widget);
- gtk_widget_set_tooltip_text (widget, _("Font Name"));
- gtk_toolbar_insert (toolbar, tool_item, 0);
- priv->font_name_combo_box = g_object_ref (widget);
- gtk_widget_show_all (GTK_WIDGET (tool_item));
-
e_binding_bind_property_full (
editor, "mode",
E_HTML_EDITOR_ACTION (editor, "paragraph-style-menu"), "visible",
@@ -1085,15 +1389,24 @@ html_editor_dispose (GObject *object)
if (self->priv->mode_change_content_cancellable)
g_cancellable_cancel (self->priv->mode_change_content_cancellable);
- g_clear_object (&self->priv->manager);
- g_clear_object (&self->priv->core_actions);
- g_clear_object (&self->priv->core_editor_actions);
- g_clear_object (&self->priv->html_actions);
- g_clear_object (&self->priv->context_actions);
- g_clear_object (&self->priv->html_context_actions);
- g_clear_object (&self->priv->language_actions);
- g_clear_object (&self->priv->spell_check_actions);
- g_clear_object (&self->priv->suggestion_actions);
+ g_clear_object (&self->priv->ui_manager);
+
+ self->priv->core_actions = NULL;
+ self->priv->core_editor_actions = NULL;
+ self->priv->html_actions = NULL;
+ self->priv->context_actions = NULL;
+ self->priv->html_context_actions = NULL;
+ self->priv->language_actions = NULL;
+ self->priv->spell_check_actions = NULL;
+ self->priv->suggestion_actions = NULL;
+
+ g_clear_object (&self->priv->emoticon_menu);
+ g_clear_object (&self->priv->recent_languages_menu);
+ g_clear_object (&self->priv->all_languages_menu);
+ g_clear_pointer (&self->priv->spell_suggest_actions, g_ptr_array_unref);
+ g_clear_pointer (&self->priv->spell_suggest_more_actions, g_ptr_array_unref);
+ g_clear_pointer (&self->priv->spell_add_actions, g_ptr_array_unref);
+ g_clear_pointer (&self->priv->spell_suggest_menus_by_code, g_hash_table_unref);
g_clear_object (&self->priv->main_menu);
g_clear_object (&self->priv->main_toolbar);
@@ -1104,14 +1417,6 @@ html_editor_dispose (GObject *object)
g_clear_object (&self->priv->edit_area);
g_clear_object (&self->priv->markdown_editor);
- g_clear_object (&self->priv->fg_color_combo_box);
- g_clear_object (&self->priv->bg_color_combo_box);
- g_clear_object (&self->priv->mode_combo_box);
- g_clear_object (&self->priv->mode_tool_item);
- g_clear_object (&self->priv->size_combo_box);
- g_clear_object (&self->priv->font_name_combo_box);
- g_clear_object (&self->priv->style_combo_box);
-
g_clear_object (&self->priv->mode_change_content_cancellable);
g_clear_pointer (&self->priv->filename, g_free);
@@ -1223,6 +1528,15 @@ e_html_editor_class_init (EHTMLEditorClass *class)
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
+
+ signals[AFTER_MODE_CHANGED] = g_signal_new (
+ "after-mode-changed",
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
}
static void
@@ -1232,36 +1546,33 @@ e_html_editor_alert_sink_init (EAlertSinkInterface *interface)
}
static void
-e_html_editor_init (EHTMLEditor *editor)
+e_html_editor_init (EHTMLEditor *self)
{
- EHTMLEditorPrivate *priv;
- gchar *filename;
- GError *error = NULL;
+ self->priv = e_html_editor_get_instance_private (self);
+ self->priv->mode = E_CONTENT_EDITOR_MODE_HTML;
+ self->priv->ui_manager = e_ui_manager_new ();
+ self->priv->core_actions = e_ui_manager_get_action_group (self->priv->ui_manager, "core");
+ self->priv->core_editor_actions = e_ui_manager_get_action_group (self->priv->ui_manager, "core-editor");
+ self->priv->html_actions = e_ui_manager_get_action_group (self->priv->ui_manager, "html");
+ self->priv->context_actions = e_ui_manager_get_action_group (self->priv->ui_manager, "core-context");
+ self->priv->html_context_actions = e_ui_manager_get_action_group (self->priv->ui_manager, "html-context");
+ self->priv->language_actions = e_ui_manager_get_action_group (self->priv->ui_manager, "language");
+ self->priv->spell_check_actions = e_ui_manager_get_action_group (self->priv->ui_manager, "spell-check");
+ self->priv->suggestion_actions = e_ui_manager_get_action_group (self->priv->ui_manager, "suggestion");
+ self->priv->cid_parts = g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, g_free, g_object_unref);
+ self->priv->content_editors = g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, g_free, NULL);
+ self->priv->content_editors_for_mode = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
- editor->priv = e_html_editor_get_instance_private (editor);
+ self->priv->emoticon_menu = g_menu_new ();
+ self->priv->recent_languages_menu = g_menu_new ();
+ self->priv->all_languages_menu = g_menu_new ();
+ self->priv->spell_suggest_actions = g_ptr_array_new_with_free_func (g_object_unref);
+ self->priv->spell_suggest_more_actions = g_ptr_array_new_with_free_func (g_object_unref);
+ self->priv->spell_add_actions = g_ptr_array_new_with_free_func (g_object_unref);
+ self->priv->spell_suggest_menus_by_code = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, e_html_editor_action_menu_pair_free);
- priv = editor->priv;
-
- priv->mode = E_CONTENT_EDITOR_MODE_HTML;
- priv->manager = gtk_ui_manager_new ();
- priv->core_actions = gtk_action_group_new ("core");
- priv->core_editor_actions = gtk_action_group_new ("core-editor");
- priv->html_actions = gtk_action_group_new ("html");
- priv->context_actions = gtk_action_group_new ("core-context");
- priv->html_context_actions = gtk_action_group_new ("html-context");
- priv->language_actions = gtk_action_group_new ("language");
- priv->spell_check_actions = gtk_action_group_new ("spell-check");
- priv->suggestion_actions = gtk_action_group_new ("suggestion");
- priv->cid_parts = g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, g_free, g_object_unref);
- priv->content_editors = g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, g_free, NULL);
- priv->content_editors_for_mode = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
-
- filename = html_editor_find_ui_file ("e-html-editor-manager.ui");
- if (!gtk_ui_manager_add_ui_from_file (priv->manager, filename, &error)) {
- g_critical ("Couldn't load builder file: %s\n", error->message);
- g_clear_error (&error);
- }
- g_free (filename);
+ g_signal_connect_object (self->priv->ui_manager, "create-item",
+ G_CALLBACK (e_html_editor_ui_manager_create_item_cb), self, 0);
}
static void
@@ -1793,6 +2104,8 @@ e_html_editor_set_mode (EHTMLEditor *editor,
if (cnt_editor) {
gboolean editor_changed = cnt_editor != editor->priv->use_content_editor;
+ e_ui_manager_freeze (editor->priv->ui_manager);
+
if (editor_changed) {
EContentEditorInterface *iface;
gboolean is_focused = FALSE;
@@ -1824,19 +2137,12 @@ e_html_editor_set_mode (EHTMLEditor *editor,
if (E_IS_MARKDOWN_EDITOR (editor->priv->use_content_editor)) {
EMarkdownEditor *markdown_editor;
- GtkToolbar *toolbar;
GSettings *settings;
markdown_editor = E_MARKDOWN_EDITOR (editor->priv->use_content_editor);
e_markdown_editor_set_preview_mode (markdown_editor, FALSE);
- toolbar = e_markdown_editor_get_action_toolbar (markdown_editor);
- gtk_container_remove (GTK_CONTAINER (toolbar), GTK_WIDGET (editor->priv->mode_tool_item));
-
- toolbar = GTK_TOOLBAR (editor->priv->edit_toolbar);
- gtk_toolbar_insert (toolbar, editor->priv->mode_tool_item, 0);
-
settings = e_util_ref_settings ("org.gnome.evolution.mail");
if (g_settings_get_boolean (settings, "composer-show-edit-toolbar"))
gtk_widget_show (GTK_WIDGET (editor->priv->edit_toolbar));
@@ -1847,13 +2153,24 @@ e_html_editor_set_mode (EHTMLEditor *editor,
gtk_widget_show (GTK_WIDGET (cnt_editor));
if (E_IS_MARKDOWN_EDITOR (cnt_editor)) {
- GtkToolbar *toolbar;
+ if (!g_object_get_data (G_OBJECT (cnt_editor), "EHTMLEditor::has-editing-mode")) {
+ GtkToolbar *toolbar;
+ GtkToolItem *tool_item;
+ GtkWidget *widget;
- toolbar = GTK_TOOLBAR (editor->priv->edit_toolbar);
- gtk_container_remove (GTK_CONTAINER (toolbar), GTK_WIDGET (editor->priv->mode_tool_item));
+ g_object_set_data (G_OBJECT (cnt_editor), "EHTMLEditor::has-editing-mode", GINT_TO_POINTER (1));
- toolbar = e_markdown_editor_get_action_toolbar (E_MARKDOWN_EDITOR (cnt_editor));
- gtk_toolbar_insert (toolbar, editor->priv->mode_tool_item, 0);
+ widget = e_action_combo_box_new_with_action (ACTION (MODE_HTML));
+ gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (widget), FALSE);
+ gtk_widget_set_tooltip_text (widget, _("Editing Mode"));
+
+ tool_item = gtk_tool_item_new ();
+ gtk_container_add (GTK_CONTAINER (tool_item), widget);
+ gtk_widget_show_all (GTK_WIDGET (tool_item));
+
+ toolbar = e_markdown_editor_get_action_toolbar (E_MARKDOWN_EDITOR (cnt_editor));
+ gtk_toolbar_insert (toolbar, tool_item, 0);
+ }
gtk_widget_hide (GTK_WIDGET (editor->priv->edit_toolbar));
}
@@ -1865,7 +2182,7 @@ e_html_editor_set_mode (EHTMLEditor *editor,
support moving between misspelled words. */
iface = E_CONTENT_EDITOR_GET_IFACE (cnt_editor);
- gtk_action_set_visible (e_html_editor_get_action (editor, "spell-check"),
+ e_ui_action_set_visible (ACTION (SPELL_CHECK),
iface && iface->spell_check_next_word && iface->spell_check_prev_word);
e_content_editor_clear_undo_redo_history (cnt_editor);
@@ -1886,6 +2203,8 @@ e_html_editor_set_mode (EHTMLEditor *editor,
g_object_set (G_OBJECT (cnt_editor), "mode", mode, NULL);
g_object_notify (G_OBJECT (editor), "mode");
+
+ e_ui_manager_thaw (editor->priv->ui_manager);
}
}
@@ -1915,14 +2234,16 @@ e_html_editor_cancel_mode_change_content_update (EHTMLEditor *editor)
* e_html_editor_get_ui_manager:
* @editor: an #EHTMLEditor
*
- * Returns #GtkUIManager that manages all the actions in the @editor.
+ * Returns an #EUIManager that manages all the actions in the @editor.
+ *
+ * Returns: (transfer none): an internal #EUIManager
*/
-GtkUIManager *
+EUIManager *
e_html_editor_get_ui_manager (EHTMLEditor *editor)
{
g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
- return editor->priv->manager;
+ return editor->priv->ui_manager;
}
/**
@@ -1930,95 +2251,55 @@ e_html_editor_get_ui_manager (EHTMLEditor *editor)
* @editor: an #EHTMLEditor
* @action_name: name of action to lookup and return
*
- * Returns: A #GtkAction matching @action_name or @NULL if no such action exists.
+ * Gets action named @action_name from the internal #EUIManager.
+ *
+ * Returns: (transfer none) (nullable): an #EUIAction matching @action_name or
+ * %NULL, if no such action exists
*/
-GtkAction *
+EUIAction *
e_html_editor_get_action (EHTMLEditor *editor,
const gchar *action_name)
{
- GtkUIManager *manager;
- GtkAction *action = NULL;
- GList *list;
+ EUIManager *ui_manager;
g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
g_return_val_if_fail (action_name != NULL, NULL);
- manager = e_html_editor_get_ui_manager (editor);
- list = gtk_ui_manager_get_action_groups (manager);
+ ui_manager = e_html_editor_get_ui_manager (editor);
- while (list != NULL && action == NULL) {
- GtkActionGroup *action_group = list->data;
-
- action = gtk_action_group_get_action (
- action_group, action_name);
-
- list = g_list_next (list);
- }
-
- g_return_val_if_fail (action != NULL, NULL);
-
- return action;
+ return e_ui_manager_get_action (ui_manager, action_name);
}
/**
- * e_html_editor_get_action_group:
+ * e_html_editor_get_ui_object:
* @editor: an #EHTMLEditor
- * @group_name: name of action group to lookup and return
+ * @object_name: an object name to get
*
- * Returns: A #GtkActionGroup matching @group_name or @NULL if not such action
- * group exists.
- */
-GtkActionGroup *
-e_html_editor_get_action_group (EHTMLEditor *editor,
- const gchar *group_name)
+ * Gets a UI object (usually a GtkWidget descendant) by its name.
+ * There are only few objects recognized, namely:
+ * E_HTML_EDITOR_UI_OBJECT_MAIN_MENU: the main menu as an #EUIMenu;
+ * E_HTML_EDITOR_UI_OBJECT_MAIN_TOOLBAR: the main toolbar as a #GtkToolbar.
+ *
+ * Returns: (transfer none) (nullable): the UI object named @object_name, or %NULL,
+ * when the name is not recognized.
+ *
+ * Since: 3.56
+ **/
+gpointer
+e_html_editor_get_ui_object (EHTMLEditor *editor,
+ const gchar *object_name)
{
- GtkUIManager *manager;
- GList *list;
+ gpointer object = NULL;
g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
- g_return_val_if_fail (group_name != NULL, NULL);
+ g_return_val_if_fail (object_name != NULL, NULL);
- manager = e_html_editor_get_ui_manager (editor);
- list = gtk_ui_manager_get_action_groups (manager);
+ if (g_strcmp0 (object_name, E_HTML_EDITOR_UI_OBJECT_MAIN_MENU) == 0)
+ object = editor->priv->main_menu;
+ else if (g_strcmp0 (object_name, E_HTML_EDITOR_UI_OBJECT_MAIN_TOOLBAR) == 0)
+ object = editor->priv->main_toolbar;
- while (list != NULL) {
- GtkActionGroup *action_group = list->data;
- const gchar *name;
-
- name = gtk_action_group_get_name (action_group);
- if (strcmp (name, group_name) == 0)
- return action_group;
-
- list = g_list_next (list);
- }
-
- return NULL;
-}
-
-GtkWidget *
-e_html_editor_get_managed_widget (EHTMLEditor *editor,
- const gchar *widget_path)
-{
- GtkUIManager *manager;
- GtkWidget *widget;
-
- g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
- g_return_val_if_fail (widget_path != NULL, NULL);
-
- manager = e_html_editor_get_ui_manager (editor);
- widget = gtk_ui_manager_get_widget (manager, widget_path);
-
- g_return_val_if_fail (widget != NULL, NULL);
-
- return widget;
-}
-
-GtkWidget *
-e_html_editor_get_style_combo_box (EHTMLEditor *editor)
-{
- g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
-
- return editor->priv->style_combo_box;
+ return object;
}
/**
diff --git a/src/e-util/e-html-editor.h b/src/e-util/e-html-editor.h
index 997f3e113f..8271feba7f 100644
--- a/src/e-util/e-html-editor.h
+++ b/src/e-util/e-html-editor.h
@@ -32,6 +32,9 @@
#include
#include
#include
+#include
+#include
+#include
/* Standard GObject macros */
#define E_TYPE_HTML_EDITOR \
@@ -54,6 +57,9 @@
#define E_HTML_EDITOR_MAX_INDENT_LEVEL 10
+#define E_HTML_EDITOR_UI_OBJECT_MAIN_MENU "main-menu"
+#define E_HTML_EDITOR_UI_OBJECT_MAIN_TOOLBAR "main-toolbar"
+
G_BEGIN_DECLS
typedef struct _EHTMLEditor EHTMLEditor;
@@ -100,18 +106,11 @@ void e_html_editor_set_mode (EHTMLEditor *editor,
void e_html_editor_cancel_mode_change_content_update
(EHTMLEditor *editor);
GtkBuilder * e_html_editor_get_builder (EHTMLEditor *editor);
-GtkUIManager * e_html_editor_get_ui_manager (EHTMLEditor *editor);
-GtkAction * e_html_editor_get_action (EHTMLEditor *editor,
+EUIManager * e_html_editor_get_ui_manager (EHTMLEditor *editor);
+EUIAction * e_html_editor_get_action (EHTMLEditor *editor,
const gchar *action_name);
-GtkActionGroup *e_html_editor_get_action_group (EHTMLEditor *editor,
- const gchar *group_name);
-GtkWidget * e_html_editor_get_widget (EHTMLEditor *editor,
- const gchar *widget_name);
-GtkWidget * e_html_editor_get_managed_widget
- (EHTMLEditor *editor,
- const gchar *widget_path);
-GtkWidget * e_html_editor_get_style_combo_box
- (EHTMLEditor *editor);
+gpointer e_html_editor_get_ui_object (EHTMLEditor *editor,
+ const gchar *object_name);
const gchar * e_html_editor_get_filename (EHTMLEditor *editor);
void e_html_editor_set_filename (EHTMLEditor *editor,
const gchar *filename);
diff --git a/src/e-util/e-mail-signature-editor.c b/src/e-util/e-mail-signature-editor.c
index 7faeba3090..6fe4d9551f 100644
--- a/src/e-util/e-mail-signature-editor.c
+++ b/src/e-util/e-mail-signature-editor.c
@@ -34,7 +34,7 @@ typedef struct _AsyncContext AsyncContext;
struct _EMailSignatureEditorPrivate {
EHTMLEditor *editor;
- GtkActionGroup *action_group;
+ EUIActionGroup *action_group;
EFocusTracker *focus_tracker;
GCancellable *cancellable;
ESourceRegistry *registry;
@@ -44,6 +44,7 @@ struct _EMailSignatureEditorPrivate {
GtkWidget *entry; /* not referenced */
EMenuBar *menu_bar;
+ GtkWidget *menu_button; /* owned by menu_bar */
};
struct _AsyncContext {
@@ -64,24 +65,6 @@ enum {
PROP_SOURCE
};
-static const gchar *ui =
-"\n"
-" \n"
-" \n"
-" \n"
-" \n"
-" "
-" \n"
-" \n"
-" \n"
-" \n"
-" \n"
-" \n"
-" \n"
-" \n"
-" \n"
-"";
-
G_DEFINE_TYPE_WITH_PRIVATE (EMailSignatureEditor, e_mail_signature_editor, GTK_TYPE_WINDOW)
static void
@@ -188,26 +171,28 @@ static gboolean
mail_signature_editor_delete_event_cb (EMailSignatureEditor *editor,
GdkEvent *event)
{
- GtkActionGroup *action_group;
- GtkAction *action;
+ EUIAction *action;
- action_group = editor->priv->action_group;
- action = gtk_action_group_get_action (action_group, "close");
- gtk_action_activate (action);
+ action = e_ui_action_group_get_action (editor->priv->action_group, "close");
+ g_action_activate (G_ACTION (action), NULL);
return TRUE;
}
static void
-action_close_cb (GtkAction *action,
- EMailSignatureEditor *window)
+action_close_cb (EUIAction *action,
+ GVariant *parametr,
+ gpointer user_data)
{
+ EMailSignatureEditor *window = user_data;
EHTMLEditor *editor;
EContentEditor *cnt_editor;
gboolean something_changed = FALSE;
const gchar *original_name;
const gchar *signature_name;
+ g_return_if_fail (E_IS_MAIL_SIGNATURE_EDITOR (window));
+
editor = e_mail_signature_editor_get_editor (window);
cnt_editor = e_html_editor_get_content_editor (editor);
@@ -224,13 +209,10 @@ action_close_cb (GtkAction *action,
GTK_WINDOW (window),
"widgets:ask-signature-changed", NULL);
if (response == GTK_RESPONSE_YES) {
- GtkActionGroup *action_group;
- GtkAction *action2;
+ EUIAction *action2;
- action_group = window->priv->action_group;
- action2 = gtk_action_group_get_action (
- action_group, "save-and-close");
- gtk_action_activate (action2);
+ action2 = e_ui_action_group_get_action (window->priv->action_group, "save-and-close");
+ g_action_activate (G_ACTION (action2), NULL);
return;
} else if (response == GTK_RESPONSE_CANCEL)
return;
@@ -283,13 +265,17 @@ mail_signature_editor_commit_ready_cb (GObject *source_object,
}
static void
-action_save_and_close_cb (GtkAction *action,
- EMailSignatureEditor *editor)
+action_save_and_close_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EMailSignatureEditor *editor = user_data;
GtkEntry *entry;
ESource *source;
gchar *display_name;
+ g_return_if_fail (E_IS_MAIL_SIGNATURE_EDITOR (editor));
+
entry = GTK_ENTRY (editor->priv->entry);
source = e_mail_signature_editor_get_source (editor);
@@ -322,29 +308,35 @@ action_save_and_close_cb (GtkAction *action,
mail_signature_editor_commit_ready_cb, NULL);
}
-static GtkActionEntry entries[] = {
+static gboolean
+e_mail_signature_editor_ui_manager_create_item_cb (EUIManager *ui_manager,
+ EUIElement *elem,
+ EUIAction *action,
+ EUIElementKind for_kind,
+ GObject **out_item,
+ gpointer user_data)
+{
+ EMailSignatureEditor *self = user_data;
+ const gchar *name;
- { "close",
- "window-close",
- N_("_Close"),
- "w",
- N_("Close"),
- G_CALLBACK (action_close_cb) },
+ g_return_val_if_fail (E_IS_MAIL_SIGNATURE_EDITOR (self), FALSE);
- { "save-and-close",
- "document-save",
- N_("_Save and Close"),
- "Return",
- N_("Save and Close"),
- G_CALLBACK (action_save_and_close_cb) },
+ name = g_action_get_name (G_ACTION (action));
- { "file-menu",
- NULL,
- N_("_File"),
- NULL,
- NULL,
- NULL }
-};
+ if (!g_str_has_prefix (name, "EMailSignatureEditor::"))
+ return FALSE;
+
+ if (for_kind == E_UI_ELEMENT_KIND_HEADERBAR) {
+ if (g_str_equal (name, "EMailSignatureEditor::menu-button"))
+ *out_item = G_OBJECT (g_object_ref (self->priv->menu_button));
+ else
+ g_warning ("%s: Unhandled headerbar action '%s'", G_STRFUNC, name);
+ } else {
+ g_warning ("%s: Unhandled element kind '%d' for action '%s'", G_STRFUNC, (gint) for_kind, name);
+ }
+
+ return TRUE;
+}
static void
mail_signature_editor_set_editor (EMailSignatureEditor *editor,
@@ -503,22 +495,64 @@ mail_signature_editor_finalize (GObject *object)
static void
mail_signature_editor_constructed (GObject *object)
{
+ static const gchar *eui =
+ ""
+ ""
+ ""
+ " "
+ ""
+ ""
+ " "
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ " "
+ ""
+ ""
+ "";
+
+ static const EUIActionEntry entries[] = {
+
+ { "close",
+ "window-close",
+ N_("_Close"),
+ "w",
+ N_("Close"),
+ action_close_cb, NULL, NULL, NULL },
+
+ { "save-and-close",
+ "document-save",
+ N_("_Save and Close"),
+ "Return",
+ N_("Save and Close"),
+ action_save_and_close_cb, NULL, NULL, NULL },
+
+ { "file-menu", NULL, N_("_File"), NULL, NULL, NULL, NULL, NULL, NULL },
+ { "EMailSignatureEditor::menu-button", NULL, N_("Menu"), NULL, NULL, NULL, NULL, NULL, NULL }
+ };
+
EMailSignatureEditor *window;
- GtkActionGroup *action_group;
EFocusTracker *focus_tracker;
EHTMLEditor *editor;
EContentEditor *cnt_editor;
- GtkUIManager *ui_manager;
+ EUIManager *ui_manager;
+ EUIAction *action;
ESource *source;
- GtkAction *action;
+ GObject *ui_item;
GtkWidget *container;
GtkWidget *widget;
- GtkWidget *button;
GtkWidget *hbox;
- GtkWidget *menu_button = NULL;
- GtkHeaderBar *header_bar;
const gchar *display_name;
- GError *error = NULL;
/* Chain up to parent's constructed() method. */
G_OBJECT_CLASS (e_mail_signature_editor_parent_class)->constructed (object);
@@ -526,33 +560,21 @@ mail_signature_editor_constructed (GObject *object)
window = E_MAIL_SIGNATURE_EDITOR (object);
editor = e_mail_signature_editor_get_editor (window);
cnt_editor = e_html_editor_get_content_editor (editor);
-
ui_manager = e_html_editor_get_ui_manager (editor);
- /* Because we are loading from a hard-coded string, there is
- * no chance of I/O errors. Failure here implies a malformed
- * UI definition. Full stop. */
- gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
- if (error != NULL)
- g_error ("%s", error->message);
+ g_signal_connect_object (ui_manager, "create-item",
+ G_CALLBACK (e_mail_signature_editor_ui_manager_create_item_cb), window, 0);
- action_group = gtk_action_group_new ("signature");
- gtk_action_group_set_translation_domain (
- action_group, GETTEXT_PACKAGE);
- gtk_action_group_add_actions (
- action_group, entries,
- G_N_ELEMENTS (entries), window);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
- window->priv->action_group = g_object_ref (action_group);
+ e_ui_manager_add_actions_with_eui_data (ui_manager, "signature", GETTEXT_PACKAGE,
+ entries, G_N_ELEMENTS (entries), window, eui);
+ window->priv->action_group = g_object_ref (e_ui_manager_get_action_group (ui_manager, "signature"));
/* Hide page properties because it is not inherited in the mail. */
action = e_html_editor_get_action (editor, "properties-page");
- gtk_action_set_visible (action, FALSE);
+ e_ui_action_set_visible (action, FALSE);
action = e_html_editor_get_action (editor, "context-properties-page");
- gtk_action_set_visible (action, FALSE);
-
- gtk_ui_manager_ensure_update (ui_manager);
+ e_ui_action_set_visible (action, FALSE);
gtk_window_set_default_size (GTK_WINDOW (window), -1, 440);
@@ -562,45 +584,30 @@ mail_signature_editor_constructed (GObject *object)
container = widget;
- widget = e_html_editor_get_managed_widget (editor, "/main-menu");
- window->priv->menu_bar = e_menu_bar_new (GTK_MENU_BAR (widget), GTK_WINDOW (window), &menu_button);
+ ui_item = e_ui_manager_create_item (ui_manager, "main-menu");
+ widget = gtk_menu_bar_new_from_model (G_MENU_MODEL (ui_item));
+ g_clear_object (&ui_item);
+
+ window->priv->menu_bar = e_menu_bar_new (GTK_MENU_BAR (widget), GTK_WINDOW (window), &window->priv->menu_button);
gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
/* Construct the main menu and toolbar. */
if (e_util_get_use_header_bar ()) {
- widget = gtk_header_bar_new ();
- gtk_widget_show (widget);
- header_bar = GTK_HEADER_BAR (widget);
- gtk_header_bar_set_show_close_button (header_bar, TRUE);
- gtk_header_bar_set_title (header_bar, _("Edit Signature"));
- gtk_window_set_titlebar (GTK_WINDOW (window), widget);
+ ui_item = e_ui_manager_create_item (ui_manager, "main-headerbar");
+ widget = GTK_WIDGET (ui_item);
+ gtk_header_bar_set_title (GTK_HEADER_BAR (widget), _("Edit Signature"));
- action = gtk_action_group_get_action (window->priv->action_group, "save-and-close");
- button = e_header_bar_button_new (_("Save"), action);
- e_header_bar_button_css_add_class (E_HEADER_BAR_BUTTON (button), "suggested-action");
- e_header_bar_button_set_show_icon_only (E_HEADER_BAR_BUTTON (button), FALSE);
- gtk_widget_show (button);
- gtk_header_bar_pack_start (header_bar, button);
-
- if (menu_button)
- gtk_header_bar_pack_end (header_bar, menu_button);
-
- widget = e_html_editor_get_managed_widget (editor, "/main-toolbar/pre-main-toolbar/save-and-close");
- gtk_widget_destroy (widget);
+ ui_item = e_ui_manager_create_item (ui_manager, "main-toolbar-with-headerbar");
} else {
gtk_window_set_title (GTK_WINDOW (window), _("Edit Signature"));
- widget = e_html_editor_get_managed_widget (editor, "/main-toolbar");
- gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
- gtk_widget_show (widget);
-
- if (menu_button) {
- g_object_ref_sink (menu_button);
- gtk_widget_destroy (menu_button);
- }
+ ui_item = e_ui_manager_create_item (ui_manager, "main-toolbar-without-headerbar");
}
+ widget = GTK_WIDGET (ui_item);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+
/* Construct the signature name entry. */
widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
diff --git a/src/e-util/e-menu-bar.c b/src/e-util/e-menu-bar.c
index cde744b70d..4dd9497813 100644
--- a/src/e-util/e-menu-bar.c
+++ b/src/e-util/e-menu-bar.c
@@ -153,6 +153,7 @@ menu_bar_dispose (GObject *menu_bar)
self->priv->delayed_hide_id = 0;
}
+ g_clear_object (&self->priv->inner_menu_bar);
g_clear_object (&self->priv->menu_button);
/* Chain up to parent's method. */
@@ -204,11 +205,11 @@ static gboolean
delayed_hide_cb (gpointer user_data)
{
EMenuBar *self = user_data;
- GtkWidget *widget = GTK_WIDGET (self->priv->inner_menu_bar);
+ GtkWidget *widget = self->priv->inner_menu_bar;
self->priv->delayed_hide_id = 0;
- if (!self->priv->visible &&
+ if (!self->priv->visible && widget &&
!self->priv->delayed_show_id) {
if (gtk_widget_get_visible (widget) &&
!gtk_menu_shell_get_selected_item (GTK_MENU_SHELL (self->priv->inner_menu_bar)))
@@ -302,7 +303,7 @@ e_menu_bar_new (GtkMenuBar *inner_menu_bar,
g_return_val_if_fail (GTK_IS_WINDOW (window), NULL);
self = g_object_new (E_TYPE_MENU_BAR, NULL);
- self->priv->inner_menu_bar = GTK_WIDGET (inner_menu_bar);
+ self->priv->inner_menu_bar = GTK_WIDGET (g_object_ref_sink (inner_menu_bar));
settings = e_util_ref_settings ("org.gnome.evolution.shell");
g_signal_connect_object (
diff --git a/src/e-util/e-menu-tool-action.c b/src/e-util/e-menu-tool-action.c
deleted file mode 100644
index 97e52765b6..0000000000
--- a/src/e-util/e-menu-tool-action.c
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * e-menu-tool-action.c
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, see .
- *
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#include "evolution-config.h"
-
-#include "e-menu-tool-action.h"
-
-G_DEFINE_TYPE (
- EMenuToolAction,
- e_menu_tool_action,
- GTK_TYPE_ACTION)
-
-static void
-e_menu_tool_action_class_init (EMenuToolActionClass *class)
-{
- GtkActionClass *action_class;
-
- action_class = GTK_ACTION_CLASS (class);
- action_class->toolbar_item_type = GTK_TYPE_MENU_TOOL_BUTTON;
-}
-
-static void
-e_menu_tool_action_init (EMenuToolAction *action)
-{
-}
-
-EMenuToolAction *
-e_menu_tool_action_new (const gchar *name,
- const gchar *label,
- const gchar *tooltip)
-{
- g_return_val_if_fail (name != NULL, NULL);
-
- return g_object_new (
- E_TYPE_MENU_TOOL_ACTION,
- "name", name,
- "label", label,
- "tooltip", tooltip,
- NULL);
-}
diff --git a/src/e-util/e-menu-tool-action.h b/src/e-util/e-menu-tool-action.h
deleted file mode 100644
index 1cb16a77aa..0000000000
--- a/src/e-util/e-menu-tool-action.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * e-menu-tool-action.h
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, see .
- *
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-/* This is a trivial GtkAction subclass that sets the toolbar
- * item type to GtkMenuToolButton instead of GtkToolButton. */
-
-#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
-#error "Only should be included directly."
-#endif
-
-#ifndef E_MENU_TOOL_ACTION_H
-#define E_MENU_TOOL_ACTION_H
-
-#include
-
-/* Standard GObject macros */
-#define E_TYPE_MENU_TOOL_ACTION \
- (e_menu_tool_action_get_type ())
-#define E_MENU_TOOL_ACTION(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST \
- ((obj), E_TYPE_MENU_TOOL_ACTION, EMenuToolAction))
-#define E_MENU_TOOL_ACTION_CLASS(cls) \
- (G_TYPE_CHECK_CLASS_CAST \
- ((cls), E_TYPE_MENU_TOOL_ACTION, EMenuToolActionClass))
-#define E_IS_MENU_TOOL_ACTION(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE \
- ((obj), E_TYPE_MENU_TOOL_ACTION))
-#define E_IS_MENU_TOOL_ACTION_CLASS(cls) \
- (G_TYPE_CHECK_CLASS_TYPE \
- ((cls), E_TYPE_MENU_TOOL_ACTION))
-#define E_MENU_TOOL_ACTION_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS \
- ((obj), E_TYPE_MENU_TOOL_ACTION, EMenuToolActionClass))
-
-G_BEGIN_DECLS
-
-typedef struct _EMenuToolAction EMenuToolAction;
-typedef struct _EMenuToolActionClass EMenuToolActionClass;
-
-struct _EMenuToolAction {
- GtkAction parent;
-};
-
-struct _EMenuToolActionClass {
- GtkActionClass parent_class;
-};
-
-GType e_menu_tool_action_get_type (void) G_GNUC_CONST;
-EMenuToolAction *
- e_menu_tool_action_new (const gchar *name,
- const gchar *label,
- const gchar *tooltip);
-
-G_END_DECLS
-
-#endif /* E_MENU_TOOL_ACTION_H */
diff --git a/src/e-util/e-menu-tool-button.c b/src/e-util/e-menu-tool-button.c
index 2f70da834b..9f489dbfa3 100644
--- a/src/e-util/e-menu-tool-button.c
+++ b/src/e-util/e-menu-tool-button.c
@@ -20,136 +20,53 @@
#include "evolution-config.h"
-#include "e-menu-tool-button.h"
#include "e-misc-utils.h"
+#include "e-ui-manager.h"
+
+#include "e-menu-tool-button.h"
struct _EMenuToolButtonPrivate {
gchar *prefer_item;
+ EUIManager *ui_manager;
};
enum {
PROP_0,
- PROP_PREFER_ITEM
+ PROP_PREFER_ITEM,
+ PROP_UI_MANAGER
};
G_DEFINE_TYPE_WITH_PRIVATE (EMenuToolButton, e_menu_tool_button, GTK_TYPE_MENU_TOOL_BUTTON)
-static GtkWidget *
-menu_tool_button_clone_image (GtkWidget *source)
+static EUIAction *
+menu_tool_button_get_prefer_item_action (EMenuToolButton *self)
{
- GtkIconSize size;
- GtkImageType image_type;
- const gchar *icon_name;
-
- /* XXX This isn't general purpose because it requires that the
- * source image be using a named icon. Somewhat surprised
- * GTK+ doesn't offer something like this. */
- image_type = gtk_image_get_storage_type (GTK_IMAGE (source));
- g_return_val_if_fail (image_type == GTK_IMAGE_ICON_NAME, NULL);
- gtk_image_get_icon_name (GTK_IMAGE (source), &icon_name, &size);
-
- return gtk_image_new_from_icon_name (icon_name, size);
-}
-
-static GtkMenuItem *
-menu_tool_button_get_prefer_menu_item (GtkMenuToolButton *menu_tool_button)
-{
- GtkWidget *menu;
- GtkMenuItem *item = NULL;
- GList *children;
- const gchar *prefer_item;
-
- menu = gtk_menu_tool_button_get_menu (menu_tool_button);
- if (!GTK_IS_MENU (menu))
+ if (!self->priv->ui_manager ||
+ !self->priv->prefer_item)
return NULL;
- children = gtk_container_get_children (GTK_CONTAINER (menu));
- if (children == NULL)
- return NULL;
-
- prefer_item = e_menu_tool_button_get_prefer_item (
- E_MENU_TOOL_BUTTON (menu_tool_button));
- if (prefer_item != NULL && *prefer_item != '\0') {
- GtkAction *action;
- GList *link;
-
- for (link = children; link != NULL; link = g_list_next (link)) {
- GtkWidget *child;
- const gchar *name;
-
- child = GTK_WIDGET (link->data);
-
- if (!GTK_IS_MENU_ITEM (child))
- continue;
-
- action = gtk_activatable_get_related_action (
- GTK_ACTIVATABLE (child));
-
- if (action != NULL)
- name = gtk_action_get_name (action);
- else
- name = gtk_widget_get_name (child);
-
- if (g_strcmp0 (name, prefer_item) == 0) {
- item = GTK_MENU_ITEM (child);
- break;
- }
- }
- }
-
- if (item == NULL)
- item = GTK_MENU_ITEM (children->data);
-
- g_list_free (children);
-
- return item;
+ return e_ui_manager_get_action (self->priv->ui_manager, self->priv->prefer_item);
}
static void
-menu_tool_button_update_button (GtkToolButton *tool_button)
+menu_tool_button_update_button (EMenuToolButton *self)
{
- GtkMenuItem *menu_item;
- GtkMenuToolButton *menu_tool_button;
- GtkImageMenuItem *image_menu_item;
- GtkAction *action;
- GtkWidget *image;
- gchar *tooltip = NULL;
+ EUIAction *action;
+ gchar *label;
- menu_tool_button = GTK_MENU_TOOL_BUTTON (tool_button);
- menu_item = menu_tool_button_get_prefer_menu_item (menu_tool_button);
- if (!GTK_IS_IMAGE_MENU_ITEM (menu_item))
+ action = menu_tool_button_get_prefer_item_action (self);
+
+ if (!action)
return;
- image_menu_item = GTK_IMAGE_MENU_ITEM (menu_item);
- image = gtk_image_menu_item_get_image (image_menu_item);
- if (!GTK_IS_IMAGE (image))
- return;
+ /* preserve the label */
+ label = g_strdup (gtk_tool_button_get_label (GTK_TOOL_BUTTON (self)));
- image = menu_tool_button_clone_image (image);
- gtk_tool_button_set_icon_widget (tool_button, image);
- gtk_widget_show (image);
+ e_ui_manager_update_item_from_action (self->priv->ui_manager, self, action);
- /* If the menu item is a proxy for a GtkAction, extract
- * the action's tooltip and use it as our own tooltip. */
- action = gtk_activatable_get_related_action (
- GTK_ACTIVATABLE (menu_item));
- if (action != NULL)
- g_object_get (action, "tooltip", &tooltip, NULL);
- gtk_widget_set_tooltip_text (GTK_WIDGET (tool_button), tooltip);
- g_free (tooltip);
-}
+ gtk_tool_button_set_label (GTK_TOOL_BUTTON (self), label);
-static void
-menu_tool_button_clicked (GtkToolButton *tool_button)
-{
- GtkMenuItem *menu_item;
- GtkMenuToolButton *menu_tool_button;
-
- menu_tool_button = GTK_MENU_TOOL_BUTTON (tool_button);
- menu_item = menu_tool_button_get_prefer_menu_item (menu_tool_button);
-
- if (GTK_IS_MENU_ITEM (menu_item))
- gtk_menu_item_activate (menu_item);
+ g_free (label);
}
static void
@@ -164,6 +81,11 @@ menu_tool_button_set_property (GObject *object,
E_MENU_TOOL_BUTTON (object),
g_value_get_string (value));
return;
+
+ case PROP_UI_MANAGER:
+ g_clear_object (&E_MENU_TOOL_BUTTON (object)->priv->ui_manager);
+ E_MENU_TOOL_BUTTON (object)->priv->ui_manager = g_value_dup_object (value);
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -181,6 +103,10 @@ menu_tool_button_get_property (GObject *object,
value, e_menu_tool_button_get_prefer_item (
E_MENU_TOOL_BUTTON (object)));
return;
+
+ case PROP_UI_MANAGER:
+ g_value_set_object (value, E_MENU_TOOL_BUTTON (object)->priv->ui_manager);
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -191,6 +117,7 @@ menu_tool_button_finalize (GObject *object)
{
EMenuToolButton *self = E_MENU_TOOL_BUTTON (object);
+ g_clear_object (&self->priv->ui_manager);
g_free (self->priv->prefer_item);
/* Chain up to parent's finalize() method. */
@@ -201,16 +128,12 @@ static void
e_menu_tool_button_class_init (EMenuToolButtonClass *class)
{
GObjectClass *object_class;
- GtkToolButtonClass *tool_button_class;
object_class = G_OBJECT_CLASS (class);
object_class->set_property = menu_tool_button_set_property;
object_class->get_property = menu_tool_button_get_property;
object_class->finalize = menu_tool_button_finalize;
- tool_button_class = GTK_TOOL_BUTTON_CLASS (class);
- tool_button_class->clicked = menu_tool_button_clicked;
-
g_object_class_install_property (
object_class,
PROP_PREFER_ITEM,
@@ -220,6 +143,16 @@ e_menu_tool_button_class_init (EMenuToolButtonClass *class)
"Name of an item to show instead of the first",
NULL,
G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_UI_MANAGER,
+ g_param_spec_object (
+ "ui-manager",
+ NULL,
+ NULL,
+ E_TYPE_UI_MANAGER,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
static void
@@ -235,9 +168,13 @@ e_menu_tool_button_init (EMenuToolButton *button)
}
GtkToolItem *
-e_menu_tool_button_new (const gchar *label)
+e_menu_tool_button_new (const gchar *label,
+ EUIManager *ui_manager)
{
- return g_object_new (E_TYPE_MENU_TOOL_BUTTON, "label", label, NULL);
+ return g_object_new (E_TYPE_MENU_TOOL_BUTTON,
+ "label", label,
+ "ui-manager", ui_manager,
+ NULL);
}
const gchar *
@@ -260,5 +197,7 @@ e_menu_tool_button_set_prefer_item (EMenuToolButton *button,
g_free (button->priv->prefer_item);
button->priv->prefer_item = g_strdup (prefer_item);
+ menu_tool_button_update_button (button);
+
g_object_notify (G_OBJECT (button), "prefer-item");
}
diff --git a/src/e-util/e-menu-tool-button.h b/src/e-util/e-menu-tool-button.h
index e8c276b565..726e6863f8 100644
--- a/src/e-util/e-menu-tool-button.h
+++ b/src/e-util/e-menu-tool-button.h
@@ -31,6 +31,8 @@
#include
+#include
+
/* Standard GObject macros */
#define E_TYPE_MENU_TOOL_BUTTON \
(e_menu_tool_button_get_type ())
@@ -66,7 +68,8 @@ struct _EMenuToolButtonClass {
};
GType e_menu_tool_button_get_type (void) G_GNUC_CONST;
-GtkToolItem * e_menu_tool_button_new (const gchar *label);
+GtkToolItem * e_menu_tool_button_new (const gchar *label,
+ EUIManager *ui_manager);
const gchar * e_menu_tool_button_get_prefer_item
(EMenuToolButton *button);
void e_menu_tool_button_set_prefer_item
diff --git a/src/e-util/e-misc-utils.c b/src/e-util/e-misc-utils.c
index 5de05e8482..f1c1eea30a 100644
--- a/src/e-util/e-misc-utils.c
+++ b/src/e-util/e-misc-utils.c
@@ -530,245 +530,6 @@ e_restore_window (GtkWindow *window,
g_object_unref (settings);
}
-/**
- * e_lookup_action:
- * @ui_manager: a #GtkUIManager
- * @action_name: the name of an action
- *
- * Returns the first #GtkAction named @action_name by traversing the
- * list of action groups in @ui_manager. If no such action exists, the
- * function emits a critical warning before returning %NULL, since this
- * probably indicates a programming error and most code is not prepared
- * to deal with lookup failures.
- *
- * Returns: the first #GtkAction named @action_name
- **/
-GtkAction *
-e_lookup_action (GtkUIManager *ui_manager,
- const gchar *action_name)
-{
- GtkAction *action = NULL;
- GList *iter;
-
- g_return_val_if_fail (GTK_IS_UI_MANAGER (ui_manager), NULL);
- g_return_val_if_fail (action_name != NULL, NULL);
-
- iter = gtk_ui_manager_get_action_groups (ui_manager);
-
- while (iter != NULL) {
- GtkActionGroup *action_group = iter->data;
-
- action = gtk_action_group_get_action (
- action_group, action_name);
- if (action != NULL)
- return action;
-
- iter = g_list_next (iter);
- }
-
- g_critical ("%s: action '%s' not found", G_STRFUNC, action_name);
-
- return NULL;
-}
-
-/**
- * e_lookup_action_group:
- * @ui_manager: a #GtkUIManager
- * @group_name: the name of an action group
- *
- * Returns the #GtkActionGroup in @ui_manager named @group_name. If no
- * such action group exists, the function emits a critical warnings before
- * returning %NULL, since this probably indicates a programming error and
- * most code is not prepared to deal with lookup failures.
- *
- * Returns: the #GtkActionGroup named @group_name
- **/
-GtkActionGroup *
-e_lookup_action_group (GtkUIManager *ui_manager,
- const gchar *group_name)
-{
- GList *iter;
-
- g_return_val_if_fail (GTK_IS_UI_MANAGER (ui_manager), NULL);
- g_return_val_if_fail (group_name != NULL, NULL);
-
- iter = gtk_ui_manager_get_action_groups (ui_manager);
-
- while (iter != NULL) {
- GtkActionGroup *action_group = iter->data;
- const gchar *name;
-
- name = gtk_action_group_get_name (action_group);
- if (strcmp (name, group_name) == 0)
- return action_group;
-
- iter = g_list_next (iter);
- }
-
- g_critical ("%s: action group '%s' not found", G_STRFUNC, group_name);
-
- return NULL;
-}
-
-/**
- * e_action_compare_by_label:
- * @action1: a #GtkAction
- * @action2: a #GtkAction
- *
- * Compares the labels for @action1 and @action2 using g_utf8_collate().
- *
- * Returns: < 0 if @action1 compares before @action2, 0 if they
- * compare equal, > 0 if @action1 compares after @action2
- **/
-gint
-e_action_compare_by_label (GtkAction *action1,
- GtkAction *action2)
-{
- gchar *label1;
- gchar *label2;
- gint result;
-
- /* XXX This is horribly inefficient but will generally only be
- * used on short lists of actions during UI construction. */
-
- if (action1 == action2)
- return 0;
-
- g_object_get (action1, "label", &label1, NULL);
- g_object_get (action2, "label", &label2, NULL);
-
- result = g_utf8_collate (label1, label2);
-
- g_free (label1);
- g_free (label2);
-
- return result;
-}
-
-/**
- * e_action_group_remove_all_actions:
- * @action_group: a #GtkActionGroup
- *
- * Removes all actions from the action group.
- **/
-void
-e_action_group_remove_all_actions (GtkActionGroup *action_group)
-{
- GList *list, *iter;
-
- /* XXX I've proposed this function for inclusion in GTK+.
- * GtkActionGroup stores actions in an internal hash
- * table and can do this more efficiently by calling
- * g_hash_table_remove_all().
- *
- * http://bugzilla.gnome.org/show_bug.cgi?id=550485 */
-
- g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
-
- list = gtk_action_group_list_actions (action_group);
- for (iter = list; iter != NULL; iter = iter->next)
- gtk_action_group_remove_action (action_group, iter->data);
- g_list_free (list);
-}
-
-/**
- * e_radio_action_get_current_action:
- * @radio_action: a #GtkRadioAction
- *
- * Returns the currently active member of the group to which @radio_action
- * belongs.
- *
- * Returns: the currently active group member
- **/
-GtkRadioAction *
-e_radio_action_get_current_action (GtkRadioAction *radio_action)
-{
- GSList *group;
- gint current_value;
-
- g_return_val_if_fail (GTK_IS_RADIO_ACTION (radio_action), NULL);
-
- group = gtk_radio_action_get_group (radio_action);
- current_value = gtk_radio_action_get_current_value (radio_action);
-
- while (group != NULL) {
- gint value;
-
- radio_action = GTK_RADIO_ACTION (group->data);
- g_object_get (radio_action, "value", &value, NULL);
-
- if (value == current_value)
- return radio_action;
-
- group = g_slist_next (group);
- }
-
- return NULL;
-}
-
-/**
- * e_action_group_add_actions_localized:
- * @action_group: a #GtkActionGroup to add @entries to
- * @translation_domain: a translation domain to use
- * to translate label and tooltip strings in @entries
- * @entries: (array length=n_entries): an array of action descriptions
- * @n_entries: the number of entries
- * @user_data: data to pass to the action callbacks
- *
- * Adds #GtkAction-s defined by @entries to @action_group, with action's
- * label and tooltip localized in the given translation domain, instead
- * of the domain set on the @action_group.
- *
- * Since: 3.4
- **/
-void
-e_action_group_add_actions_localized (GtkActionGroup *action_group,
- const gchar *translation_domain,
- const GtkActionEntry *entries,
- guint n_entries,
- gpointer user_data)
-{
- GtkActionGroup *tmp_group;
- GList *list, *iter;
- gint ii;
-
- g_return_if_fail (action_group != NULL);
- g_return_if_fail (entries != NULL);
- g_return_if_fail (n_entries > 0);
- g_return_if_fail (translation_domain != NULL);
- g_return_if_fail (*translation_domain);
-
- tmp_group = gtk_action_group_new ("temporary-group");
- gtk_action_group_set_translation_domain (tmp_group, translation_domain);
- gtk_action_group_add_actions (tmp_group, entries, n_entries, user_data);
-
- list = gtk_action_group_list_actions (tmp_group);
- for (iter = list; iter != NULL; iter = iter->next) {
- GtkAction *action = GTK_ACTION (iter->data);
- const gchar *action_name;
-
- g_object_ref (action);
-
- action_name = gtk_action_get_name (action);
-
- for (ii = 0; ii < n_entries; ii++) {
- if (g_strcmp0 (entries[ii].name, action_name) == 0) {
- gtk_action_group_remove_action (
- tmp_group, action);
- gtk_action_group_add_action_with_accel (
- action_group, action,
- entries[ii].accelerator);
- break;
- }
- }
-
- g_object_unref (action);
- }
-
- g_list_free (list);
- g_object_unref (tmp_group);
-}
-
/**
* e_builder_get_widget:
* @builder: a #GtkBuilder
@@ -836,102 +597,6 @@ e_load_ui_builder_definition (GtkBuilder *builder,
}
}
-static gdouble
-e_get_ui_manager_definition_file_version (const gchar *filename)
-{
- xmlDocPtr doc;
- xmlNode *root;
- gdouble version = -1.0;
-
- g_return_val_if_fail (filename != NULL, version);
-
- doc = e_xml_parse_file (filename);
- if (!doc)
- return version;
-
- root = xmlDocGetRootElement (doc);
- if (root && g_strcmp0 ((const gchar *) root->name, "ui") == 0) {
- version = e_xml_get_double_prop_by_name_with_default (root, (const xmlChar *) "evolution-ui-version", -1.0);
- }
-
- xmlFreeDoc (doc);
-
- return version;
-}
-
-static gchar *
-e_pick_ui_manager_definition_file (const gchar *basename)
-{
- gchar *system_filename, *user_filename;
- gdouble system_version, user_version;
-
- g_return_val_if_fail (basename != NULL, NULL);
-
- system_filename = g_build_filename (EVOLUTION_UIDIR, basename, NULL);
- user_filename = g_build_filename (e_get_user_config_dir (), "ui", basename, NULL);
-
- if (!g_file_test (user_filename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
- g_free (user_filename);
-
- return system_filename;
- }
-
- user_version = e_get_ui_manager_definition_file_version (user_filename);
- system_version = e_get_ui_manager_definition_file_version (system_filename);
-
- /* Versions are equal and the system version is a positive number */
- if (user_version - system_version >= -1e-9 &&
- user_version - system_version <= 1e-9 &&
- system_version > 1e-9) {
- g_free (system_filename);
-
- return user_filename;
- }
-
- g_warning ("User's UI file '%s' version (%.1f) doesn't match expected version (%.1f), skipping it. Either correct the version or remove the file.",
- user_filename, user_version, system_version);
-
- g_free (user_filename);
-
- return system_filename;
-}
-
-/**
- * e_load_ui_manager_definition:
- * @ui_manager: a #GtkUIManager
- * @basename: basename of the UI definition file
- *
- * Loads a UI definition into @ui_manager from Evolution's UI directory.
- * Failure here is fatal, since the application can't function without
- * its UI definitions.
- *
- * Returns: The merge ID for the merged UI. The merge ID can be used to
- * unmerge the UI with gtk_ui_manager_remove_ui().
- **/
-guint
-e_load_ui_manager_definition (GtkUIManager *ui_manager,
- const gchar *basename)
-{
- gchar *filename;
- guint merge_id;
- GError *error = NULL;
-
- g_return_val_if_fail (GTK_IS_UI_MANAGER (ui_manager), 0);
- g_return_val_if_fail (basename != NULL, 0);
-
- filename = e_pick_ui_manager_definition_file (basename);
- merge_id = gtk_ui_manager_add_ui_from_file (
- ui_manager, filename, &error);
- g_free (filename);
-
- if (error != NULL) {
- g_error ("%s: %s", basename, error->message);
- g_warn_if_reached ();
- }
-
- return merge_id;
-}
-
/* Helper for e_categories_add_change_hook() */
static void
categories_changed_cb (GObject *useless_opaque_object,
@@ -5165,3 +4830,99 @@ e_util_call_malloc_trim_limited (void)
e_util_call_malloc_trim ();
}
}
+
+static gboolean
+e_util_detach_menu_on_idle_cb (gpointer user_data)
+{
+ GtkMenu *menu = user_data;
+
+ gtk_menu_detach (menu);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+e_util_menu_deactivate_cb (GtkMenu *menu,
+ gpointer user_data)
+{
+ g_return_if_fail (GTK_IS_MENU (menu));
+
+ g_signal_handlers_disconnect_by_func (menu, e_util_menu_deactivate_cb, user_data);
+
+ g_idle_add_full (G_PRIORITY_LOW, e_util_detach_menu_on_idle_cb, g_object_ref (menu), g_object_unref);
+}
+
+/**
+ * e_util_connect_menu_detach_after_deactivate:
+ * @menu: a #GtkMenu
+ *
+ * Connects a signal handler on a "deactivate" signal of the @menu and
+ * calls gtk_menu_detach() after the handler is invoked, which can cause
+ * destroy of the @menu.
+ *
+ * As the #GAction-s are not executed immediately by the GTK, the detach can be
+ * called only later, not in the deactivate signal handler. This function makes
+ * it simpler and consistent to detach (and possibly free) the @menu after the user
+ * dismisses it either by clicking elsewhere or by picking an item from it.
+ *
+ * Since: 3.56
+ **/
+void
+e_util_connect_menu_detach_after_deactivate (GtkMenu *menu)
+{
+ g_return_if_fail (GTK_IS_MENU (menu));
+
+ g_signal_connect (
+ menu, "deactivate",
+ G_CALLBACK (e_util_menu_deactivate_cb), NULL);
+}
+
+/**
+ * e_util_ignore_accel_for_focused:
+ * @focused: (nullable): a focused #GtkWidget, or %NULL
+ *
+ * Returns whether an accel key press should be ignored, due to the @focused
+ * might use it. Returns %FALSE, when the @focused is %NULL.
+ *
+ * Returns: whether an accel key press should be ignored
+ *
+ * Since: 3.56
+ **/
+gboolean
+e_util_ignore_accel_for_focused (GtkWidget *focused)
+{
+ if (!focused)
+ return FALSE;
+
+ if ((GTK_IS_ENTRY (focused) || GTK_IS_EDITABLE (focused) ||
+ (GTK_IS_TREE_VIEW (focused) && gtk_tree_view_get_search_column (GTK_TREE_VIEW (focused)) >= 0))) {
+ GdkEvent *event;
+ gboolean ignore = TRUE;
+
+ event = gtk_get_current_event ();
+ if (event) {
+ GdkModifierType modifs = 0;
+ guint keyval = 0;
+ gboolean can_process;
+
+ /* multi-key presses are always allowed */
+ can_process = gdk_event_get_state (event, &modifs) &&
+ (modifs & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK)) != 0;
+
+ if (!can_process && gdk_event_get_keyval (event, &keyval)) {
+ /* function keys are allowed (they are used as shortcuts in the mail view) */
+ can_process = keyval == GDK_KEY_F1 || keyval == GDK_KEY_F2 || keyval == GDK_KEY_F3 || keyval == GDK_KEY_F4 ||
+ keyval == GDK_KEY_F5 || keyval == GDK_KEY_F6 || keyval == GDK_KEY_F7 || keyval == GDK_KEY_F8 ||
+ keyval == GDK_KEY_F9 || keyval == GDK_KEY_F10 || keyval == GDK_KEY_F11 || keyval == GDK_KEY_F12;
+ }
+
+ g_clear_pointer (&event, gdk_event_free);
+
+ ignore = !can_process;
+ }
+
+ return ignore;
+ }
+
+ return FALSE;
+}
diff --git a/src/e-util/e-misc-utils.h b/src/e-util/e-misc-utils.h
index 8170ef1976..cdb258ad4a 100644
--- a/src/e-util/e-misc-utils.h
+++ b/src/e-util/e-misc-utils.h
@@ -60,28 +60,10 @@ void e_display_help (GtkWindow *parent,
void e_restore_window (GtkWindow *window,
const gchar *settings_path,
ERestoreWindowFlags flags);
-GtkAction * e_lookup_action (GtkUIManager *ui_manager,
- const gchar *action_name);
-GtkActionGroup *e_lookup_action_group (GtkUIManager *ui_manager,
- const gchar *group_name);
-gint e_action_compare_by_label (GtkAction *action1,
- GtkAction *action2);
-void e_action_group_remove_all_actions
- (GtkActionGroup *action_group);
-GtkRadioAction *e_radio_action_get_current_action
- (GtkRadioAction *radio_action);
-void e_action_group_add_actions_localized
- (GtkActionGroup *action_group,
- const gchar *translation_domain,
- const GtkActionEntry *entries,
- guint n_entries,
- gpointer user_data);
GtkWidget * e_builder_get_widget (GtkBuilder *builder,
const gchar *widget_name);
void e_load_ui_builder_definition (GtkBuilder *builder,
const gchar *basename);
-guint e_load_ui_manager_definition (GtkUIManager *ui_manager,
- const gchar *basename);
void e_categories_add_change_hook (GHookFunc func,
gpointer object);
@@ -360,6 +342,9 @@ void e_open_map_uri (GtkWindow *parent,
gboolean e_util_link_requires_reference (const gchar *href,
const gchar *text);
void e_util_call_malloc_trim_limited (void);
+void e_util_connect_menu_detach_after_deactivate
+ (GtkMenu *menu);
+gboolean e_util_ignore_accel_for_focused (GtkWidget *focused);
G_END_DECLS
diff --git a/src/e-util/e-plugin-ui.c b/src/e-util/e-plugin-ui.c
index 83ded1e271..002cc07deb 100644
--- a/src/e-util/e-plugin-ui.c
+++ b/src/e-util/e-plugin-ui.c
@@ -16,55 +16,30 @@
#include "evolution-config.h"
-#include "e-plugin-ui.h"
-
#include
+#include "e-ui-manager.h"
+
+#include "e-plugin-ui.h"
+
#define E_PLUGIN_UI_DEFAULT_FUNC "e_plugin_ui_init"
#define E_PLUGIN_UI_HOOK_CLASS_ID "org.gnome.evolution.ui:1.0"
struct _EPluginUIHookPrivate {
- /* Table of GtkUIManager ID's to UI definitions.
- *
- * For example:
- *
- *
- *
- * ... UI definition ...
- *
- *
- *
- * Results in:
- *
- * g_hash_table_insert (
- * ui_definitions,
- * "org.gnome.evolution.foo",
- * "... UI definition ...");
- *
- * See https://docs.gtk.org/gtk3/class.UIManager.html
- * for more information about UI definitions. Note: the tag is
- * optional.
- */
- GHashTable *ui_definitions;
-
- /* Table of GtkUIManager ID's to callback function names.
+ /* Table of EUIManager ID's to callback function names.
*
* This stores the optional "callback" attribute in the
* element. If not specified, it defaults to "e_plugin_ui_init".
*
- * This is useful when extending the UI of multiple GtkUIManager IDs
+ * This is useful when extending the UI of multiple EUIManager IDs
* from a single plugin.
*
* For example:
*
*
- *
- * ...
- *
- *
- * ...
- *
+ *
+ *
*
*
* Results in:
@@ -77,115 +52,38 @@ struct _EPluginUIHookPrivate {
*/
GHashTable *callbacks;
- /* The registry is the heart of EPluginUI. It tracks GtkUIManager
- * instances, GtkUIManager IDs, and UI merge IDs as a hash table of
- * hash tables:
- *
- * GtkUIManager instance -> GtkUIManager ID -> UI Merge ID
- *
- * A GtkUIManager instance and ID form a unique key for looking up
- * UI merge IDs. The reason both are needed is because the same
- * GtkUIManager instance can be registered under multiple IDs.
- *
- * This is done primarily to support shell views, which share a
- * common GtkUIManager instance for a particular shell window.
- * Each shell view registers the same GtkUIManager instance under
- * a unique ID:
- *
- * "org.gnome.evolution.mail" }
- * "org.gnome.evolution.contacts" } aliases for a common
- * "org.gnome.evolution.calendar" } GtkUIManager instance
- * "org.gnome.evolution.memos" }
- * "org.gnome.evolution.tasks" }
- *
- * Note: The shell window also registers the same GtkUIManager
- * instance as "org.gnome.evolution.shell".
- *
- * This way, plugins that extend a shell view's UI will follow the
- * merging and unmerging of the shell view automatically.
- *
- * The presence or absence of GtkUIManager IDs in the registry is
- * significant. Presence of a (instance, ID) pair indicates that
- * UI manager is active, absence indicates inactive. Furthermore,
- * a non-zero merge ID for an active UI manager indicates the
- * plugin is enabled. Zero indicates disabled.
- *
- * Here's a quick scenario to illustrate:
- *
- * Suppose we have a plugin that extends the mail shell view UI.
- * Its EPlugin definition file has this section:
- *
- *
- *
- * ... UI definition ...
- *
- *
- *
- * The plugin is enabled and the active shell view is "mail".
- * Let "ManagerA" denote the common GtkUIManager instance for
- * this shell window. Here's what happens to the registry as
- * the user performs various actions;
- *
- * - Initial State Merge ID
- * V
- * { "ManagerA", { "org.gnome.evolution.mail", 3 } }
- *
- * - User Disables the Plugin
- *
- * { "ManagerA", { "org.gnome.evolution.mail", 0 } }
- *
- * - User Enables the Plugin
- *
- * { "ManagerA", { "org.gnome.evolution.mail", 4 } }
- *
- * - User Switches to Calendar View
- *
- * { "ManagerA", { } }
- *
- * - User Disables the Plugin
- *
- * { "ManagerA", { } }
- *
- * - User Switches to Mail View
- *
- * { "ManagerA", { "org.gnome.evolution.mail", 0 } }
- *
- * - User Enables the Plugin
- *
- * { "ManagerA", { "org.gnome.evolution.mail", 5 } }
+ /* The registry is the heart of EPluginUI. It tracks EUIManager
+ * instances.
*/
- GHashTable *registry;
+ GPtrArray *registry;
};
G_DEFINE_TYPE_WITH_PRIVATE (EPluginUIHook, e_plugin_ui_hook, E_TYPE_PLUGIN_HOOK)
static void
plugin_ui_hook_unregister_manager (EPluginUIHook *hook,
- GtkUIManager *ui_manager)
+ EUIManager *ui_manager)
{
- GHashTable *registry;
-
/* Note: Manager may already be finalized. */
- registry = hook->priv->registry;
- g_hash_table_remove (registry, ui_manager);
+ g_ptr_array_remove (hook->priv->registry, ui_manager);
}
static void
plugin_ui_hook_register_manager (EPluginUIHook *hook,
- GtkUIManager *ui_manager,
+ EUIManager *ui_manager,
const gchar *id,
gpointer user_data)
{
EPlugin *plugin;
EPluginUIInitFunc func;
- GHashTable *registry;
- GHashTable *hash_table;
const gchar *func_name;
plugin = ((EPluginHook *) hook)->plugin;
- hash_table = hook->priv->callbacks;
- func_name = g_hash_table_lookup (hash_table, id);
+ if (!g_hash_table_contains (hook->priv->callbacks, id))
+ return;
+
+ func_name = g_hash_table_lookup (hook->priv->callbacks, id);
if (func_name == NULL)
func_name = E_PLUGIN_UI_DEFAULT_FUNC;
@@ -200,7 +98,7 @@ plugin_ui_hook_register_manager (EPluginUIHook *hook,
}
/* Pass the manager and user_data to the plugin's callback function.
- * The plugin should install whatever GtkActions and GtkActionGroups
+ * The plugin should install whatever EUIAction-s and UI definitions
* are neccessary to implement the actions in its UI definition. */
if (!func (ui_manager, user_data))
return;
@@ -209,193 +107,26 @@ plugin_ui_hook_register_manager (EPluginUIHook *hook,
G_OBJECT (ui_manager), (GWeakNotify)
plugin_ui_hook_unregister_manager, hook);
- registry = hook->priv->registry;
- hash_table = g_hash_table_lookup (registry, ui_manager);
-
- if (hash_table == NULL) {
- hash_table = g_hash_table_new_full (
- g_str_hash, g_str_equal,
- (GDestroyNotify) g_free,
- (GDestroyNotify) NULL);
- g_hash_table_insert (registry, ui_manager, hash_table);
- }
-}
-
-static guint
-plugin_ui_hook_merge_ui (EPluginUIHook *hook,
- GtkUIManager *ui_manager,
- const gchar *id)
-{
- GHashTable *hash_table;
- const gchar *ui_definition;
- guint merge_id;
- GError *error = NULL;
-
- hash_table = hook->priv->ui_definitions;
- ui_definition = g_hash_table_lookup (hash_table, id);
- g_return_val_if_fail (ui_definition != NULL, 0);
-
- merge_id = gtk_ui_manager_add_ui_from_string (
- ui_manager, ui_definition, -1, &error);
-
- if (error != NULL) {
- g_warning ("%s", error->message);
- g_error_free (error);
- }
-
- return merge_id;
-}
-
-static void
-plugin_ui_enable_manager (EPluginUIHook *hook,
- GtkUIManager *ui_manager,
- const gchar *id)
-{
- GHashTable *hash_table;
- GHashTable *ui_definitions;
- GList *keys;
-
- hash_table = hook->priv->registry;
- hash_table = g_hash_table_lookup (hash_table, ui_manager);
-
- if (hash_table == NULL)
- return;
-
- if (id != NULL)
- keys = g_list_prepend (NULL, (gpointer) id);
- else
- keys = g_hash_table_get_keys (hash_table);
-
- ui_definitions = hook->priv->ui_definitions;
-
- while (keys != NULL) {
- guint merge_id;
- gpointer data;
-
- id = keys->data;
- keys = g_list_delete_link (keys, keys);
-
- if (g_hash_table_lookup (ui_definitions, id) == NULL)
- continue;
-
- data = g_hash_table_lookup (hash_table, id);
- merge_id = GPOINTER_TO_UINT (data);
-
- if (merge_id > 0)
- continue;
-
- if (((EPluginHook *) hook)->plugin->enabled)
- merge_id = plugin_ui_hook_merge_ui (
- hook, ui_manager, id);
-
- /* Merge ID will be 0 on error, which is what we want. */
- data = GUINT_TO_POINTER (merge_id);
- g_hash_table_insert (hash_table, g_strdup (id), data);
- }
-}
-
-static void
-plugin_ui_disable_manager (EPluginUIHook *hook,
- GtkUIManager *ui_manager,
- const gchar *id,
- gboolean remove)
-{
- GHashTable *hash_table;
- GHashTable *ui_definitions;
- GList *keys;
-
- hash_table = hook->priv->registry;
- hash_table = g_hash_table_lookup (hash_table, ui_manager);
-
- if (hash_table == NULL)
- return;
-
- if (id != NULL)
- keys = g_list_prepend (NULL, (gpointer) id);
- else
- keys = g_hash_table_get_keys (hash_table);
-
- ui_definitions = hook->priv->ui_definitions;
-
- while (keys != NULL) {
- guint merge_id;
- gpointer data;
-
- id = keys->data;
- keys = g_list_delete_link (keys, keys);
-
- if (g_hash_table_lookup (ui_definitions, id) == NULL)
- continue;
-
- data = g_hash_table_lookup (hash_table, id);
- merge_id = GPOINTER_TO_UINT (data);
-
- /* Merge ID could be 0 if the plugin is disabled. */
- if (merge_id > 0) {
- gtk_ui_manager_remove_ui (ui_manager, merge_id);
- gtk_ui_manager_ensure_update (ui_manager);
- }
-
- if (remove)
- g_hash_table_remove (hash_table, id);
- else
- g_hash_table_insert (hash_table, g_strdup (id), NULL);
- }
-}
-
-static void
-plugin_ui_enable_hook (EPluginUIHook *hook)
-{
- GHashTable *hash_table;
- GHashTableIter iter;
- gpointer key;
-
- /* Enable all GtkUIManagers for this hook. */
-
- hash_table = hook->priv->registry;
- g_hash_table_iter_init (&iter, hash_table);
-
- while (g_hash_table_iter_next (&iter, &key, NULL)) {
- GtkUIManager *ui_manager = key;
- plugin_ui_enable_manager (hook, ui_manager, NULL);
- }
-}
-
-static void
-plugin_ui_disable_hook (EPluginUIHook *hook)
-{
- GHashTable *hash_table;
- GHashTableIter iter;
- gpointer key;
-
- /* Disable all GtkUIManagers for this hook. */
-
- hash_table = hook->priv->registry;
- g_hash_table_iter_init (&iter, hash_table);
-
- while (g_hash_table_iter_next (&iter, &key, NULL)) {
- GtkUIManager *ui_manager = key;
- plugin_ui_disable_manager (hook, ui_manager, NULL, FALSE);
- }
+ if (!g_ptr_array_find (hook->priv->registry, ui_manager, NULL))
+ g_ptr_array_add (hook->priv->registry, ui_manager);
}
static void
plugin_ui_hook_finalize (GObject *object)
{
EPluginUIHook *self = E_PLUGIN_UI_HOOK (object);
- GHashTableIter iter;
- gpointer ui_manager;
+ guint ii;
- /* Remove weak reference callbacks to GtkUIManagers. */
- g_hash_table_iter_init (&iter, self->priv->registry);
- while (g_hash_table_iter_next (&iter, &ui_manager, NULL))
+ /* Remove weak reference callbacks to EUIManager-s. */
+ for (ii = 0; ii < self->priv->registry->len; ii++) {
+ EUIManager *ui_manager = g_ptr_array_index (self->priv->registry, ii);
g_object_weak_unref (
G_OBJECT (ui_manager), (GWeakNotify)
plugin_ui_hook_unregister_manager, object);
+ }
- g_hash_table_destroy (self->priv->ui_definitions);
g_hash_table_destroy (self->priv->callbacks);
- g_hash_table_destroy (self->priv->registry);
+ g_ptr_array_free (self->priv->registry, TRUE);
/* Chain up to parent's method. */
G_OBJECT_CLASS (e_plugin_ui_hook_parent_class)->finalize (object);
@@ -433,14 +164,12 @@ plugin_ui_hook_construct (EPluginHook *hook,
}
callback = e_plugin_xml_prop (node, "callback");
- if (callback != NULL)
- g_hash_table_insert (
- self->priv->callbacks,
- g_strdup (id), callback);
+ g_hash_table_insert (self->priv->callbacks, id, callback);
content = g_string_sized_new (1024);
- /* Extract the XML content below */
+ /* Extract the XML content below . There cannot be any since 3.56,
+ thus claim a warning if there is */
buffer = xmlBufferCreate ();
for (child = node->children; child != NULL; child = child->next) {
xmlNodeDump (buffer, node->doc, child, 2, 1);
@@ -448,10 +177,16 @@ plugin_ui_hook_construct (EPluginHook *hook,
g_string_append (content, temp);
}
- g_hash_table_insert (
- self->priv->ui_definitions,
- id, g_string_free (content, FALSE));
+ g_string_replace (content, "\r" ,"", 0);
+ g_string_replace (content, "\n" ,"", 0);
+ g_string_replace (content, " " ,"", 0);
+ if (content->len) {
+ g_warning ("UI definitions cannot be part of .eplug files anymore. Add your UI with actions in the e_plugin_ui_init() instead. Plugin: %s",
+ plugin ? plugin->id : "Unknown");
+ }
+
+ g_string_free (content, TRUE);
xmlBufferFree (buffer);
}
@@ -462,10 +197,6 @@ static void
plugin_ui_hook_enable (EPluginHook *hook,
gint state)
{
- if (state)
- plugin_ui_enable_hook (E_PLUGIN_UI_HOOK (hook));
- else
- plugin_ui_disable_hook (E_PLUGIN_UI_HOOK (hook));
}
static void
@@ -486,39 +217,26 @@ e_plugin_ui_hook_class_init (EPluginUIHookClass *class)
static void
e_plugin_ui_hook_init (EPluginUIHook *hook)
{
- GHashTable *ui_definitions;
GHashTable *callbacks;
- GHashTable *registry;
-
- ui_definitions = g_hash_table_new_full (
- g_str_hash, g_str_equal,
- (GDestroyNotify) g_free,
- (GDestroyNotify) g_free);
callbacks = g_hash_table_new_full (
g_str_hash, g_str_equal,
(GDestroyNotify) g_free,
(GDestroyNotify) g_free);
- registry = g_hash_table_new_full (
- g_direct_hash, g_direct_equal,
- (GDestroyNotify) NULL,
- (GDestroyNotify) g_hash_table_destroy);
-
hook->priv = e_plugin_ui_hook_get_instance_private (hook);
- hook->priv->ui_definitions = ui_definitions;
hook->priv->callbacks = callbacks;
- hook->priv->registry = registry;
+ hook->priv->registry = g_ptr_array_new ();
}
void
-e_plugin_ui_register_manager (GtkUIManager *ui_manager,
+e_plugin_ui_register_manager (EUIManager *ui_manager,
const gchar *id,
gpointer user_data)
{
GSList *plugin_list;
- g_return_if_fail (GTK_IS_UI_MANAGER (ui_manager));
+ g_return_if_fail (E_IS_UI_MANAGER (ui_manager));
g_return_if_fail (id != NULL);
/* Loop over all installed plugins. */
@@ -532,83 +250,12 @@ e_plugin_ui_register_manager (GtkUIManager *ui_manager,
/* Look for hooks of type EPluginUIHook. */
for (iter = plugin->hooks; iter != NULL; iter = iter->next) {
EPluginUIHook *hook = iter->data;
- GHashTable *hash_table;
if (!E_IS_PLUGIN_UI_HOOK (hook))
continue;
- hash_table = hook->priv->ui_definitions;
-
- /* Check if the hook has a UI definition
- * for the GtkUIManager being registered. */
- if (g_hash_table_lookup (hash_table, id) == NULL)
- continue;
-
/* Register the manager with the hook. */
- plugin_ui_hook_register_manager (
- hook, ui_manager, id, user_data);
- }
-
- g_object_unref (plugin);
- }
-}
-
-void
-e_plugin_ui_enable_manager (GtkUIManager *ui_manager,
- const gchar *id)
-{
- GSList *plugin_list;
-
- g_return_if_fail (GTK_IS_UI_MANAGER (ui_manager));
- g_return_if_fail (id != NULL);
-
- /* Loop over all installed plugins. */
- plugin_list = e_plugin_list_plugins ();
- while (plugin_list != NULL) {
- EPlugin *plugin = plugin_list->data;
- GSList *iter;
-
- plugin_list = g_slist_remove (plugin_list, plugin);
-
- /* Look for hooks of type EPluginUIHook. */
- for (iter = plugin->hooks; iter != NULL; iter = iter->next) {
- EPluginUIHook *hook = iter->data;
-
- if (!E_IS_PLUGIN_UI_HOOK (hook))
- continue;
-
- plugin_ui_enable_manager (hook, ui_manager, id);
- }
-
- g_object_unref (plugin);
- }
-}
-
-void
-e_plugin_ui_disable_manager (GtkUIManager *ui_manager,
- const gchar *id)
-{
- GSList *plugin_list;
-
- g_return_if_fail (GTK_IS_UI_MANAGER (ui_manager));
- g_return_if_fail (id != NULL);
-
- /* Loop over all installed plugins. */
- plugin_list = e_plugin_list_plugins ();
- while (plugin_list != NULL) {
- EPlugin *plugin = plugin_list->data;
- GSList *iter;
-
- plugin_list = g_slist_remove (plugin_list, plugin);
-
- /* Look for hooks of type EPluginUIHook. */
- for (iter = plugin->hooks; iter != NULL; iter = iter->next) {
- EPluginUIHook *hook = iter->data;
-
- if (!E_IS_PLUGIN_UI_HOOK (hook))
- continue;
-
- plugin_ui_disable_manager (hook, ui_manager, id, TRUE);
+ plugin_ui_hook_register_manager (hook, ui_manager, id, user_data);
}
g_object_unref (plugin);
diff --git a/src/e-util/e-plugin-ui.h b/src/e-util/e-plugin-ui.h
index fb9b02e746..1d49c66759 100644
--- a/src/e-util/e-plugin-ui.h
+++ b/src/e-util/e-plugin-ui.h
@@ -24,6 +24,7 @@
#include
#include
+#include
/* Standard GObject macros */
#define E_TYPE_PLUGIN_UI_HOOK \
@@ -61,18 +62,14 @@ struct _EPluginUIHookClass {
/* Plugins with "org.gnome.evolution.ui" hooks should define a
* function named e_plugin_ui_init() having this signature. */
-typedef gboolean (*EPluginUIInitFunc) (GtkUIManager *ui_manager,
+typedef gboolean (*EPluginUIInitFunc) (EUIManager *ui_manager,
gpointer user_data);
GType e_plugin_ui_hook_get_type (void) G_GNUC_CONST;
-void e_plugin_ui_register_manager (GtkUIManager *ui_manager,
+void e_plugin_ui_register_manager (EUIManager *ui_manager,
const gchar *id,
gpointer user_data);
-void e_plugin_ui_enable_manager (GtkUIManager *ui_manager,
- const gchar *id);
-void e_plugin_ui_disable_manager (GtkUIManager *ui_manager,
- const gchar *id);
G_END_DECLS
diff --git a/src/e-util/e-popup-action.c b/src/e-util/e-popup-action.c
deleted file mode 100644
index 480613be32..0000000000
--- a/src/e-util/e-popup-action.c
+++ /dev/null
@@ -1,392 +0,0 @@
-/*
- * e-popup-action.c
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, see .
- *
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#include "evolution-config.h"
-
-#include "e-popup-action.h"
-
-#include
-
-enum {
- PROP_0,
- PROP_RELATED_ACTION,
- PROP_USE_ACTION_APPEARANCE
-};
-
-struct _EPopupActionPrivate {
- GtkAction *related_action;
- gboolean use_action_appearance;
- gulong activate_handler_id;
- gulong notify_handler_id;
-};
-
-/* Forward Declarations */
-static void e_popup_action_activatable_init (GtkActivatableIface *iface);
-
-G_DEFINE_TYPE_WITH_CODE (EPopupAction, e_popup_action, GTK_TYPE_ACTION,
- G_ADD_PRIVATE (EPopupAction)
- G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE, e_popup_action_activatable_init))
-
-static void
-popup_action_notify_cb (GtkAction *action,
- GParamSpec *pspec,
- GtkActivatable *activatable)
-{
- GtkActivatableIface *iface;
-
- iface = GTK_ACTIVATABLE_GET_IFACE (activatable);
- g_return_if_fail (iface->update != NULL);
-
- iface->update (activatable, action, pspec->name);
-}
-
-static GtkAction *
-popup_action_get_related_action (EPopupAction *popup_action)
-{
- return popup_action->priv->related_action;
-}
-
-static void
-popup_action_set_related_action (EPopupAction *popup_action,
- GtkAction *related_action)
-{
- GtkActivatable *activatable;
-
- /* Do not call gtk_activatable_do_set_related_action() because
- * it assumes the activatable object is a widget and tries to add
- * it to the related actions's proxy list. Instead we'll just do
- * the relevant steps manually. */
-
- activatable = GTK_ACTIVATABLE (popup_action);
-
- if (related_action == popup_action->priv->related_action)
- return;
-
- if (related_action != NULL)
- g_object_ref (related_action);
-
- if (popup_action->priv->related_action != NULL) {
- g_signal_handler_disconnect (
- popup_action,
- popup_action->priv->activate_handler_id);
- g_signal_handler_disconnect (
- popup_action->priv->related_action,
- popup_action->priv->notify_handler_id);
- popup_action->priv->activate_handler_id = 0;
- popup_action->priv->notify_handler_id = 0;
- g_object_unref (popup_action->priv->related_action);
- }
-
- popup_action->priv->related_action = related_action;
-
- if (related_action != NULL) {
- popup_action->priv->activate_handler_id =
- g_signal_connect_swapped (
- popup_action, "activate",
- G_CALLBACK (gtk_action_activate),
- related_action);
- popup_action->priv->notify_handler_id =
- g_signal_connect (
- related_action, "notify",
- G_CALLBACK (popup_action_notify_cb),
- popup_action);
- gtk_activatable_sync_action_properties (
- activatable, related_action);
- } else
- gtk_action_set_visible (GTK_ACTION (popup_action), FALSE);
-
- g_object_notify (G_OBJECT (popup_action), "related-action");
-}
-
-static gboolean
-popup_action_get_use_action_appearance (EPopupAction *popup_action)
-{
- return popup_action->priv->use_action_appearance;
-}
-
-static void
-popup_action_set_use_action_appearance (EPopupAction *popup_action,
- gboolean use_action_appearance)
-{
- GtkActivatable *activatable;
- GtkAction *related_action;
-
- if (popup_action->priv->use_action_appearance == use_action_appearance)
- return;
-
- popup_action->priv->use_action_appearance = use_action_appearance;
-
- g_object_notify (G_OBJECT (popup_action), "use-action-appearance");
-
- activatable = GTK_ACTIVATABLE (popup_action);
- related_action = popup_action_get_related_action (popup_action);
- gtk_activatable_sync_action_properties (activatable, related_action);
-}
-
-static void
-popup_action_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- switch (property_id) {
- case PROP_RELATED_ACTION:
- popup_action_set_related_action (
- E_POPUP_ACTION (object),
- g_value_get_object (value));
- return;
-
- case PROP_USE_ACTION_APPEARANCE:
- popup_action_set_use_action_appearance (
- E_POPUP_ACTION (object),
- g_value_get_boolean (value));
- return;
- }
-
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-}
-
-static void
-popup_action_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec)
-{
- switch (property_id) {
- case PROP_RELATED_ACTION:
- g_value_set_object (
- value,
- popup_action_get_related_action (
- E_POPUP_ACTION (object)));
- return;
-
- case PROP_USE_ACTION_APPEARANCE:
- g_value_set_boolean (
- value,
- popup_action_get_use_action_appearance (
- E_POPUP_ACTION (object)));
- return;
- }
-
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-}
-
-static void
-popup_action_dispose (GObject *object)
-{
- EPopupAction *self = E_POPUP_ACTION (object);
-
- if (self->priv->related_action != NULL) {
- g_signal_handler_disconnect (
- object,
- self->priv->activate_handler_id);
- g_signal_handler_disconnect (
- self->priv->related_action,
- self->priv->notify_handler_id);
- g_clear_object (&self->priv->related_action);
- }
-
- /* Chain up to parent's dispose() method. */
- G_OBJECT_CLASS (e_popup_action_parent_class)->dispose (object);
-}
-
-static void
-popup_action_update (GtkActivatable *activatable,
- GtkAction *action,
- const gchar *property_name)
-{
- GObjectClass *class;
- GParamSpec *pspec;
- GValue *value;
-
- /* Ignore "action-group" changes" */
- if (strcmp (property_name, "action-group") == 0)
- return;
-
- /* Ignore "visible" changes. */
- if (strcmp (property_name, "visible") == 0)
- return;
-
- value = g_slice_new0 (GValue);
- class = G_OBJECT_GET_CLASS (action);
- pspec = g_object_class_find_property (class, property_name);
- g_value_init (value, pspec->value_type);
-
- g_object_get_property (G_OBJECT (action), property_name, value);
-
- if (strcmp (property_name, "sensitive") == 0)
- property_name = "visible";
- else if (!gtk_activatable_get_use_action_appearance (activatable))
- goto exit;
-
- g_object_set_property (G_OBJECT (activatable), property_name, value);
-
-exit:
- g_value_unset (value);
- g_slice_free (GValue, value);
-}
-
-static void
-popup_action_sync_action_properties (GtkActivatable *activatable,
- GtkAction *action)
-{
- if (action == NULL)
- return;
-
- /* XXX GTK+ 2.18 is still missing accessor functions for
- * "hide-if-empty" and "visible-overflown" properties.
- * These are rarely used so we'll skip them for now. */
-
- /* A popup action is never shown as insensitive. */
- gtk_action_set_sensitive (GTK_ACTION (activatable), TRUE);
-
- gtk_action_set_visible (
- GTK_ACTION (activatable),
- gtk_action_get_sensitive (action));
-
- gtk_action_set_visible_horizontal (
- GTK_ACTION (activatable),
- gtk_action_get_visible_horizontal (action));
-
- gtk_action_set_visible_vertical (
- GTK_ACTION (activatable),
- gtk_action_get_visible_vertical (action));
-
- gtk_action_set_is_important (
- GTK_ACTION (activatable),
- gtk_action_get_is_important (action));
-
- if (!gtk_activatable_get_use_action_appearance (activatable))
- return;
-
- gtk_action_set_label (
- GTK_ACTION (activatable),
- gtk_action_get_label (action));
-
- gtk_action_set_short_label (
- GTK_ACTION (activatable),
- gtk_action_get_short_label (action));
-
- gtk_action_set_tooltip (
- GTK_ACTION (activatable),
- gtk_action_get_tooltip (action));
-
- gtk_action_set_stock_id (
- GTK_ACTION (activatable),
- gtk_action_get_stock_id (action));
-
- gtk_action_set_gicon (
- GTK_ACTION (activatable),
- gtk_action_get_gicon (action));
-
- gtk_action_set_icon_name (
- GTK_ACTION (activatable),
- gtk_action_get_icon_name (action));
-}
-
-static void
-e_popup_action_class_init (EPopupActionClass *class)
-{
- GObjectClass *object_class;
-
- object_class = G_OBJECT_CLASS (class);
- object_class->set_property = popup_action_set_property;
- object_class->get_property = popup_action_get_property;
- object_class->dispose = popup_action_dispose;
-
- g_object_class_override_property (
- object_class,
- PROP_RELATED_ACTION,
- "related-action");
-
- g_object_class_override_property (
- object_class,
- PROP_USE_ACTION_APPEARANCE,
- "use-action-appearance");
-}
-
-static void
-e_popup_action_init (EPopupAction *popup_action)
-{
- popup_action->priv = e_popup_action_get_instance_private (popup_action);
- popup_action->priv->use_action_appearance = TRUE;
-
- /* Remain invisible until we have a related action. */
- gtk_action_set_visible (GTK_ACTION (popup_action), FALSE);
-}
-
-static void
-e_popup_action_activatable_init (GtkActivatableIface *iface)
-{
- iface->update = popup_action_update;
- iface->sync_action_properties = popup_action_sync_action_properties;
-}
-
-EPopupAction *
-e_popup_action_new (const gchar *name)
-{
- g_return_val_if_fail (name != NULL, NULL);
-
- return g_object_new (E_TYPE_POPUP_ACTION, "name", name, NULL);
-}
-
-void
-e_action_group_add_popup_actions (GtkActionGroup *action_group,
- const EPopupActionEntry *entries,
- guint n_entries)
-{
- guint ii;
-
- g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
-
- for (ii = 0; ii < n_entries; ii++) {
- EPopupAction *popup_action;
- GtkAction *related_action;
- const gchar *label;
-
- label = gtk_action_group_translate_string (
- action_group, entries[ii].label);
-
- related_action = gtk_action_group_get_action (
- action_group, entries[ii].related);
-
- if (related_action == NULL) {
- g_warning (
- "Related action '%s' not found in "
- "action group '%s'", entries[ii].related,
- gtk_action_group_get_name (action_group));
- continue;
- }
-
- popup_action = e_popup_action_new (entries[ii].name);
-
- gtk_activatable_set_related_action (
- GTK_ACTIVATABLE (popup_action), related_action);
-
- if (label != NULL && *label != '\0')
- gtk_action_set_label (
- GTK_ACTION (popup_action), label);
-
- gtk_action_group_add_action (
- action_group, GTK_ACTION (popup_action));
-
- g_object_unref (popup_action);
- }
-}
diff --git a/src/e-util/e-popup-action.h b/src/e-util/e-popup-action.h
deleted file mode 100644
index 8ebdcf035c..0000000000
--- a/src/e-util/e-popup-action.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * e-popup-action.h
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, see .
- *
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-/* A popup action is an action that lives in a popup menu. It proxies an
- * equivalent action in the main menu, with two differences:
- *
- * 1) If the main menu action is insensitive, the popup action is invisible.
- * 2) The popup action may have a different label than the main menu action.
- *
- * To use:
- *
- * Create an array of EPopupActionEntry structs. Add the main menu actions
- * that serve as related actions for the popup actions to an action group
- * first. Then pass the same action group and the EPopupActionEntry array
- * to e_action_group_add_popup_actions() to add popup actions.
- */
-
-#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
-#error "Only should be included directly."
-#endif
-
-#ifndef E_POPUP_ACTION_H
-#define E_POPUP_ACTION_H
-
-#include
-
-/* Standard GObject macros */
-#define E_TYPE_POPUP_ACTION \
- (e_popup_action_get_type ())
-#define E_POPUP_ACTION(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST \
- ((obj), E_TYPE_POPUP_ACTION, EPopupAction))
-#define E_POPUP_ACTION_CLASS(cls) \
- (G_TYPE_CHECK_CLASS_CAST \
- ((cls), E_TYPE_POPUP_ACTION, EPopupActionClass))
-#define E_IS_POPUP_ACTION(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE \
- ((obj), E_TYPE_POPUP_ACTION))
-#define E_IS_POPUP_ACTION_CLASS(cls) \
- (G_TYPE_CHECK_CLASS_TYPE \
- ((cls), E_TYPE_POPUP_ACTION))
-#define E_POPUP_ACTION_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS \
- ((obj), E_TYPE_POPUP_ACTION, EPopupActionClass))
-
-G_BEGIN_DECLS
-
-typedef struct _EPopupAction EPopupAction;
-typedef struct _EPopupActionClass EPopupActionClass;
-typedef struct _EPopupActionPrivate EPopupActionPrivate;
-typedef struct _EPopupActionEntry EPopupActionEntry;
-
-struct _EPopupAction {
- GtkAction parent;
- EPopupActionPrivate *priv;
-};
-
-struct _EPopupActionClass {
- GtkActionClass parent_class;
-};
-
-struct _EPopupActionEntry {
- const gchar *name;
- const gchar *label; /* optional: overrides the related action */
- const gchar *related; /* name of the related action */
-};
-
-GType e_popup_action_get_type (void) G_GNUC_CONST;
-EPopupAction * e_popup_action_new (const gchar *name);
-
-void e_action_group_add_popup_actions
- (GtkActionGroup *action_group,
- const EPopupActionEntry *entries,
- guint n_entries);
-
-G_END_DECLS
-
-#endif /* E_POPUP_ACTION_H */
diff --git a/src/e-util/e-proxy-selector.c b/src/e-util/e-proxy-selector.c
index c974f2c1b6..5812532e69 100644
--- a/src/e-util/e-proxy-selector.c
+++ b/src/e-util/e-proxy-selector.c
@@ -170,7 +170,7 @@ proxy_selector_remove_source_cb (GObject *object,
static gboolean
proxy_selector_action_add_cb (EProxySelector *selector,
- GtkAction *action)
+ EUIAction *action)
{
AsyncContext *async_context;
ESourceRegistry *registry;
@@ -217,7 +217,7 @@ proxy_selector_action_add_cb (EProxySelector *selector,
static gboolean
proxy_selector_action_remove_cb (EProxySelector *selector,
- GtkAction *action)
+ EUIAction *action)
{
ESource *selected_source;
@@ -457,7 +457,7 @@ proxy_selector_constructed (GObject *object)
GtkCellRenderer *renderer;
GtkTreeSelection *selection;
GtkListStore *list_store;
- GtkAction *action;
+ EUIAction *action;
const gchar *tooltip;
gulong handler_id;
@@ -477,12 +477,12 @@ proxy_selector_constructed (GObject *object)
action = e_tree_view_frame_lookup_toolbar_action (
tree_view_frame, E_TREE_VIEW_FRAME_ACTION_ADD);
tooltip = _("Create a new proxy profile");
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_tooltip (action, tooltip);
action = e_tree_view_frame_lookup_toolbar_action (
tree_view_frame, E_TREE_VIEW_FRAME_ACTION_REMOVE);
tooltip = _("Delete the selected proxy profile");
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_tooltip (action, tooltip);
/* Configure the tree view column. */
@@ -524,7 +524,7 @@ proxy_selector_update_toolbar_actions (ETreeViewFrame *tree_view_frame)
{
EProxySelector *selector;
ESource *selected;
- GtkAction *action;
+ EUIAction *action;
gboolean sensitive;
selector = E_PROXY_SELECTOR (tree_view_frame);
@@ -533,7 +533,7 @@ proxy_selector_update_toolbar_actions (ETreeViewFrame *tree_view_frame)
action = e_tree_view_frame_lookup_toolbar_action (
tree_view_frame, E_TREE_VIEW_FRAME_ACTION_REMOVE);
sensitive = e_source_get_removable (selected);
- gtk_action_set_sensitive (action, sensitive);
+ e_ui_action_set_sensitive (action, sensitive);
g_object_unref (selected);
diff --git a/src/e-util/e-table-column-selector.c b/src/e-util/e-table-column-selector.c
index c648c9fc04..c30b47938f 100644
--- a/src/e-util/e-table-column-selector.c
+++ b/src/e-util/e-table-column-selector.c
@@ -217,7 +217,7 @@ table_column_selector_constructed (GObject *object)
{
ETableColumnSelector *selector;
ETreeViewFrame *tree_view_frame;
- GtkAction *action;
+ EUIAction *action;
GtkTreeView *tree_view;
GtkTreeModel *tree_model;
GtkTreeSelection *selection;
@@ -243,36 +243,36 @@ table_column_selector_constructed (GObject *object)
action = e_tree_view_frame_lookup_toolbar_action (
tree_view_frame, E_TREE_VIEW_FRAME_ACTION_ADD);
- gtk_action_set_visible (action, FALSE);
+ e_ui_action_set_visible (action, FALSE);
action = e_tree_view_frame_lookup_toolbar_action (
tree_view_frame, E_TREE_VIEW_FRAME_ACTION_REMOVE);
- gtk_action_set_visible (action, FALSE);
+ e_ui_action_set_visible (action, FALSE);
action = e_tree_view_frame_lookup_toolbar_action (
tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_TOP);
tooltip = _("Move selected column names to top");
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_tooltip (action, tooltip);
action = e_tree_view_frame_lookup_toolbar_action (
tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_UP);
tooltip = _("Move selected column names up one row");
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_tooltip (action, tooltip);
action = e_tree_view_frame_lookup_toolbar_action (
tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_DOWN);
tooltip = _("Move selected column names down one row");
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_tooltip (action, tooltip);
action = e_tree_view_frame_lookup_toolbar_action (
tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_BOTTOM);
tooltip = _("Move selected column names to bottom");
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_tooltip (action, tooltip);
action = e_tree_view_frame_lookup_toolbar_action (
tree_view_frame, E_TREE_VIEW_FRAME_ACTION_SELECT_ALL);
tooltip = _("Select all column names");
- gtk_action_set_tooltip (action, tooltip);
+ e_ui_action_set_tooltip (action, tooltip);
/* Configure the tree view columns. */
diff --git a/src/e-util/e-tree-view-frame.c b/src/e-util/e-tree-view-frame.c
index e1fcded19c..e4adeadbf8 100644
--- a/src/e-util/e-tree-view-frame.c
+++ b/src/e-util/e-tree-view-frame.c
@@ -40,57 +40,57 @@
/**
* E_TREE_VIEW_FRAME_ACTION_ADD:
*
- * The #GtkAction name for the "add" toolbar button.
+ * The #EUIAction name for the "add" toolbar button.
*
- * Use e_tree_view_frame_lookup_toolbar_action() to obtain the #GtkAction.
+ * Use e_tree_view_frame_lookup_toolbar_action() to obtain the #EUAction.
**/
/**
* E_TREE_VIEW_FRAME_ACTION_REMOVE:
*
- * The #GtkAction name for the "remove" toolbar button.
+ * The #EUIAction name for the "remove" toolbar button.
*
- * Use e_tree_view_frame_lookup_toolbar_action() to obtain the #GtkAction.
+ * Use e_tree_view_frame_lookup_toolbar_action() to obtain the #EUIAction.
**/
/**
* E_TREE_VIEW_FRAME_ACTION_MOVE_TOP:
*
- * The #GtkAction name for the "move selected items to top" button.
+ * The #EUIAction name for the "move selected items to top" button.
*
- * Use e_tree_view_frame_lookup_toolbar_action() to obtain the #GtkAction.
+ * Use e_tree_view_frame_lookup_toolbar_action() to obtain the #EUIAction.
**/
/**
* E_TREE_VIEW_FRAME_ACTION_MOVE_UP:
*
- * The #GtkAction name for the "move selected items up" button.
+ * The #EUIAction name for the "move selected items up" button.
*
- * Use e_tree_view_frame_lookup_toolbar_action() to obtain the #GtkAction.
+ * Use e_tree_view_frame_lookup_toolbar_action() to obtain the #EUIAction.
**/
/**
* E_TREE_VIEW_FRAME_ACTION_MOVE_DOWN:
*
- * The #GtkAction name for the "move selected items down" button.
+ * The #EUIAction name for the "move selected items down" button.
*
- * Use e_tree_view_frame_lookup_toolbar_action() to obtain the #GtkAction.
+ * Use e_tree_view_frame_lookup_toolbar_action() to obtain the #EUIAction.
**/
/**
* E_TREE_VIEW_FRAME_ACTION_MOVE_BOTTOM:
*
- * The #GtkAction name for the "move selected items to bottom" button.
+ * The #EUIAction name for the "move selected items to bottom" button.
*
- * Use e_tree_view_frame_lookup_toolbar_action() to obtain the #GtkAction.
+ * Use e_tree_view_frame_lookup_toolbar_action() to obtain the #EUIAction.
**/
/**
* E_TREE_VIEW_FRAME_ACTION_SELECT_ALL:
*
- * The #GtkAction name for the "select all" button.
+ * The #EUIAction name for the "select all" button.
*
- * Use e_tree_view_frame_lookup_toolbar_action() to obtain the #GtkAction.
+ * Use e_tree_view_frame_lookup_toolbar_action() to obtain the #EUIAction.
**/
struct _ETreeViewFramePrivate {
@@ -102,7 +102,7 @@ struct _ETreeViewFramePrivate {
GtkWidget *scrolled_window;
GtkWidget *inline_toolbar;
- GHashTable *tool_item_ht;
+ GHashTable *actions_ht; /* gchar *name ~> EUIAction * */
GtkPolicyType hscrollbar_policy;
GtkPolicyType vscrollbar_policy;
@@ -135,19 +135,15 @@ tree_view_frame_append_action (ETreeViewFrame *tree_view_frame,
const gchar *action_name,
const gchar *icon_name)
{
- GtkAction *action;
- GIcon *icon;
+ EUIAction *action;
- icon = g_themed_icon_new_with_default_fallbacks (icon_name);
+ action = e_ui_action_new ("tree-view-frame", action_name, NULL);
- action = g_object_new (
- GTK_TYPE_ACTION,
- "name", action_name, "gicon", icon, NULL);
+ e_ui_action_set_icon_name (action, icon_name);
e_tree_view_frame_insert_toolbar_action (tree_view_frame, action, -1);
g_object_unref (action);
- g_object_unref (icon);
}
static void
@@ -396,23 +392,6 @@ tree_view_frame_action_select_all (ETreeViewFrame *tree_view_frame)
gtk_tree_selection_select_all (selection);
}
-static void
-tree_view_frame_action_activate_cb (GtkAction *action,
- ETreeViewFrame *tree_view_frame)
-{
- GQuark detail;
- const gchar *action_name;
- gboolean handled = FALSE;
-
- action_name = gtk_action_get_name (action);
- detail = g_quark_from_string (action_name);
-
- g_signal_emit (
- tree_view_frame,
- signals[TOOLBAR_ACTION_ACTIVATE], detail,
- action, &handled);
-}
-
static void
tree_view_frame_notify_reorderable_cb (GtkTreeView *tree_view,
GParamSpec *pspec,
@@ -520,7 +499,7 @@ tree_view_frame_dispose (GObject *object)
g_clear_object (&self->priv->scrolled_window);
g_clear_object (&self->priv->inline_toolbar);
- g_hash_table_remove_all (self->priv->tool_item_ht);
+ g_hash_table_remove_all (self->priv->actions_ht);
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (e_tree_view_frame_parent_class)->dispose (object);
@@ -531,7 +510,7 @@ tree_view_frame_finalize (GObject *object)
{
ETreeViewFrame *self = E_TREE_VIEW_FRAME (object);
- g_hash_table_destroy (self->priv->tool_item_ht);
+ g_hash_table_destroy (self->priv->actions_ht);
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (e_tree_view_frame_parent_class)->finalize (object);
@@ -625,11 +604,11 @@ tree_view_frame_constructed (GObject *object)
static gboolean
tree_view_frame_toolbar_action_activate (ETreeViewFrame *tree_view_frame,
- GtkAction *action)
+ EUIAction *action)
{
const gchar *action_name;
- action_name = gtk_action_get_name (action);
+ action_name = g_action_get_name (G_ACTION (action));
g_return_val_if_fail (action_name != NULL, FALSE);
if (g_str_equal (action_name, E_TREE_VIEW_FRAME_ACTION_MOVE_TOP)) {
@@ -663,7 +642,7 @@ tree_view_frame_toolbar_action_activate (ETreeViewFrame *tree_view_frame,
static void
tree_view_frame_update_toolbar_actions (ETreeViewFrame *tree_view_frame)
{
- GtkAction *action;
+ EUIAction *action;
GtkTreeView *tree_view;
GtkTreeModel *tree_model;
GtkTreeSelection *selection;
@@ -696,36 +675,36 @@ tree_view_frame_update_toolbar_actions (ETreeViewFrame *tree_view_frame)
tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_TOP);
visible = gtk_tree_view_get_reorderable (tree_view);
sensitive = (n_selected_rows > 0 && !first_row_selected);
- gtk_action_set_visible (action, visible);
- gtk_action_set_sensitive (action, sensitive);
+ e_ui_action_set_visible (action, visible);
+ e_ui_action_set_sensitive (action, sensitive);
action = e_tree_view_frame_lookup_toolbar_action (
tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_UP);
visible = gtk_tree_view_get_reorderable (tree_view);
sensitive = (n_selected_rows > 0 && !first_row_selected);
- gtk_action_set_visible (action, visible);
- gtk_action_set_sensitive (action, sensitive);
+ e_ui_action_set_visible (action, visible);
+ e_ui_action_set_sensitive (action, sensitive);
action = e_tree_view_frame_lookup_toolbar_action (
tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_DOWN);
visible = gtk_tree_view_get_reorderable (tree_view);
sensitive = (n_selected_rows > 0 && !last_row_selected);
- gtk_action_set_visible (action, visible);
- gtk_action_set_sensitive (action, sensitive);
+ e_ui_action_set_visible (action, visible);
+ e_ui_action_set_sensitive (action, sensitive);
action = e_tree_view_frame_lookup_toolbar_action (
tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_BOTTOM);
visible = gtk_tree_view_get_reorderable (tree_view);
sensitive = (n_selected_rows > 0 && !last_row_selected);
- gtk_action_set_visible (action, visible);
- gtk_action_set_sensitive (action, sensitive);
+ e_ui_action_set_visible (action, visible);
+ e_ui_action_set_sensitive (action, sensitive);
action = e_tree_view_frame_lookup_toolbar_action (
tree_view_frame, E_TREE_VIEW_FRAME_ACTION_SELECT_ALL);
visible = (selection_mode == GTK_SELECTION_MULTIPLE);
sensitive = (n_selected_rows < n_rows);
- gtk_action_set_visible (action, visible);
- gtk_action_set_sensitive (action, sensitive);
+ e_ui_action_set_visible (action, visible);
+ e_ui_action_set_sensitive (action, sensitive);
}
static void
@@ -740,10 +719,8 @@ e_tree_view_frame_class_init (ETreeViewFrameClass *class)
object_class->finalize = tree_view_frame_finalize;
object_class->constructed = tree_view_frame_constructed;
- class->toolbar_action_activate =
- tree_view_frame_toolbar_action_activate;
- class->update_toolbar_actions =
- tree_view_frame_update_toolbar_actions;
+ class->toolbar_action_activate = tree_view_frame_toolbar_action_activate;
+ class->update_toolbar_actions = tree_view_frame_update_toolbar_actions;
g_object_class_install_property (
object_class,
@@ -800,12 +777,12 @@ e_tree_view_frame_class_init (ETreeViewFrameClass *class)
/**
* ETreeViewFrame::toolbar-action-activate:
* @tree_view_frame: the #ETreeViewFrame that received the signal
- * @action: the #GtkAction that was activated
+ * @action: the #EUIAction that was activated
*
* Emitted when a toolbar action is activated.
*
* This signal supports "::detail" appendices to the signal name,
- * where the "detail" part is the #GtkAction #GtkAction:name. So
+ * where the "detail" part is the action name. So
* you can connect a signal handler to a particular action.
**/
signals[TOOLBAR_ACTION_ACTIVATE] = g_signal_new (
@@ -818,15 +795,15 @@ e_tree_view_frame_class_init (ETreeViewFrameClass *class)
g_signal_accumulator_true_handled,
NULL, NULL,
G_TYPE_BOOLEAN, 1,
- GTK_TYPE_ACTION);
+ E_TYPE_UI_ACTION);
/**
* ETreeViewFrame::update-toolbar-actions:
* @tree_view_frame: the #ETreeViewFrame that received the signal
*
* Requests toolbar actions be updated, usually in response to a
- * #GtkTreeSelection change. Handlers should update #GtkAction
- * properties like #GtkAction:visible and #GtkAction:sensitive
+ * #GtkTreeSelection change. Handlers should update action
+ * properties like #EUIAction:visible and #EUIAction:sensitive
* based on the current #ETreeViewFrame:tree-view state.
**/
signals[UPDATE_TOOLBAR_ACTIONS] = g_signal_new (
@@ -843,17 +820,8 @@ e_tree_view_frame_class_init (ETreeViewFrameClass *class)
static void
e_tree_view_frame_init (ETreeViewFrame *tree_view_frame)
{
- GHashTable *tool_item_ht;
-
- tool_item_ht = g_hash_table_new_full (
- (GHashFunc) g_str_hash,
- (GEqualFunc) g_str_equal,
- (GDestroyNotify) g_free,
- (GDestroyNotify) g_object_unref);
-
tree_view_frame->priv = e_tree_view_frame_get_instance_private (tree_view_frame);
-
- tree_view_frame->priv->tool_item_ht = tool_item_ht;
+ tree_view_frame->priv->actions_ht = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
}
/**
@@ -1067,10 +1035,32 @@ e_tree_view_frame_set_vscrollbar_policy (ETreeViewFrame *tree_view_frame,
g_object_notify (G_OBJECT (tree_view_frame), "vscrollbar-policy");
}
+static void
+tree_view_frame_tool_item_clicked_cb (GtkToolButton *tool_button,
+ gpointer user_data)
+{
+ ETreeViewFrame *self = user_data;
+ EUIAction *action;
+ GQuark detail;
+ const gchar *action_name;
+ gboolean handled = FALSE;
+
+ action = g_object_get_data (G_OBJECT (tool_button), "tree-view-frame-action");
+ g_return_if_fail (action != NULL);
+
+ action_name = g_action_get_name (G_ACTION (action));
+ detail = g_quark_from_string (action_name);
+
+ g_signal_emit (self, signals[TOOLBAR_ACTION_ACTIVATE], detail, action, &handled);
+
+ if (!handled)
+ g_action_activate (G_ACTION (action), NULL);
+}
+
/**
* e_tree_view_frame_insert_toolbar_action:
* @tree_view_frame: an #ETreeViewFrame
- * @action: a #GtkAction
+ * @action: an #EUIAction
* @position: the position of the new action
*
* Generates a #GtkToolItem from @action and inserts it into the inline
@@ -1080,74 +1070,80 @@ e_tree_view_frame_set_vscrollbar_policy (ETreeViewFrame *tree_view_frame,
**/
void
e_tree_view_frame_insert_toolbar_action (ETreeViewFrame *tree_view_frame,
- GtkAction *action,
- gint position)
+ EUIAction *action,
+ gint position)
{
GtkToolbar *toolbar;
- GtkWidget *tool_item;
- GHashTable *tool_item_ht;
+ GtkToolItem *tool_item;
const gchar *action_name;
g_return_if_fail (E_IS_TREE_VIEW_FRAME (tree_view_frame));
- g_return_if_fail (GTK_IS_ACTION (action));
+ g_return_if_fail (E_IS_UI_ACTION (action));
- action_name = gtk_action_get_name (action);
+ action_name = g_action_get_name (G_ACTION (action));
g_return_if_fail (action_name != NULL);
- tool_item_ht = tree_view_frame->priv->tool_item_ht;
toolbar = GTK_TOOLBAR (tree_view_frame->priv->inline_toolbar);
- if (g_hash_table_contains (tool_item_ht, action_name)) {
- g_warning (
- "%s: Duplicate action name '%s'",
- G_STRFUNC, action_name);
+ if (g_hash_table_contains (tree_view_frame->priv->actions_ht, action_name)) {
+ g_warning ("%s: Duplicate action name '%s'", G_STRFUNC, action_name);
return;
}
- tool_item = gtk_action_create_tool_item (action);
- g_return_if_fail (GTK_IS_TOOL_ITEM (tool_item));
+ tool_item = gtk_tool_button_new (NULL, NULL);
+ gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (tool_item), e_ui_action_get_icon_name (action));
+ gtk_tool_button_set_use_underline (GTK_TOOL_BUTTON (tool_item), TRUE);
+ g_object_set_data_full (G_OBJECT (tool_item), "tree-view-frame-action",
+ g_object_ref (action), g_object_unref);
+
+ e_binding_bind_property (
+ action, "label",
+ tool_item, "label",
+ G_BINDING_SYNC_CREATE);
+
+ e_binding_bind_property (
+ action, "tooltip",
+ tool_item, "tooltip-text",
+ G_BINDING_SYNC_CREATE);
+
+ e_binding_bind_property (
+ action, "sensitive",
+ tool_item, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ e_binding_bind_property (
+ action, "visible",
+ tool_item, "visible",
+ G_BINDING_SYNC_CREATE);
gtk_toolbar_insert (toolbar, GTK_TOOL_ITEM (tool_item), position);
- g_hash_table_insert (
- tool_item_ht,
- g_strdup (action_name),
- g_object_ref (tool_item));
+ g_hash_table_insert (tree_view_frame->priv->actions_ht, (gpointer) g_action_get_name (G_ACTION (action)), g_object_ref (action));
g_signal_connect (
- action, "activate",
- G_CALLBACK (tree_view_frame_action_activate_cb),
+ tool_item, "clicked",
+ G_CALLBACK (tree_view_frame_tool_item_clicked_cb),
tree_view_frame);
}
/**
* e_tree_view_frame_lookup_toolbar_action:
* @tree_view_frame: an #ETreeViewFrame
- * @action_name: a #GtkAction name
+ * @action_name: an #EUIAction name
*
* Returns the toolbar action named @action_name, or %NULL if no such
* toolbar action exists.
*
- * Returns: a #GtkAction, or %NULL
+ * Returns: an #EUIAction, or %NULL
**/
-GtkAction *
+EUIAction *
e_tree_view_frame_lookup_toolbar_action (ETreeViewFrame *tree_view_frame,
const gchar *action_name)
{
- GHashTable *tool_item_ht;
- GtkActivatable *activatable;
- GtkAction *action = NULL;
-
g_return_val_if_fail (E_IS_TREE_VIEW_FRAME (tree_view_frame), NULL);
g_return_val_if_fail (action_name != NULL, NULL);
- tool_item_ht = tree_view_frame->priv->tool_item_ht;
- activatable = g_hash_table_lookup (tool_item_ht, action_name);
-
- if (GTK_IS_ACTIVATABLE (activatable))
- action = gtk_activatable_get_related_action (activatable);
-
- return action;
+ return g_hash_table_lookup (tree_view_frame->priv->actions_ht, action_name);
}
/**
@@ -1165,4 +1161,3 @@ e_tree_view_frame_update_toolbar_actions (ETreeViewFrame *tree_view_frame)
g_signal_emit (tree_view_frame, signals[UPDATE_TOOLBAR_ACTIONS], 0);
}
-
diff --git a/src/e-util/e-tree-view-frame.h b/src/e-util/e-tree-view-frame.h
index 9f7883d346..b83774650b 100644
--- a/src/e-util/e-tree-view-frame.h
+++ b/src/e-util/e-tree-view-frame.h
@@ -24,6 +24,8 @@
#include
+#include
+
/* Standard GObject macros */
#define E_TYPE_TREE_VIEW_FRAME \
(e_tree_view_frame_get_type ())
@@ -74,7 +76,7 @@ struct _ETreeViewFrameClass {
/* Signals */
gboolean (*toolbar_action_activate)
(ETreeViewFrame *tree_view_frame,
- GtkAction *action);
+ EUIAction *action);
void (*update_toolbar_actions)
(ETreeViewFrame *tree_view_frame);
};
@@ -104,9 +106,9 @@ void e_tree_view_frame_set_vscrollbar_policy
GtkPolicyType vscrollbar_policy);
void e_tree_view_frame_insert_toolbar_action
(ETreeViewFrame *tree_view_frame,
- GtkAction *action,
+ EUIAction *action,
gint position);
-GtkAction * e_tree_view_frame_lookup_toolbar_action
+EUIAction * e_tree_view_frame_lookup_toolbar_action
(ETreeViewFrame *tree_view_frame,
const gchar *action_name);
void e_tree_view_frame_update_toolbar_actions
@@ -115,4 +117,3 @@ void e_tree_view_frame_update_toolbar_actions
G_END_DECLS
#endif /* E_TREE_VIEW_FRAME_H */
-
diff --git a/src/e-util/e-ui-action-group.c b/src/e-util/e-ui-action-group.c
new file mode 100644
index 0000000000..13f678f55f
--- /dev/null
+++ b/src/e-util/e-ui-action-group.c
@@ -0,0 +1,665 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * SPDX-FileCopyrightText: (C) 2024 Red Hat (www.redhat.com>
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "evolution-config.h"
+
+#include
+#include
+
+#include "e-ui-action-group.h"
+#include "e-ui-action.h"
+
+/**
+ * SECTION: e-ui-action-group
+ * @include: e-util/e-util.h
+ * @short_description: Group of UI actions
+ *
+ * The #EUIActionGroup is a named group of #EUIAction actions. The group
+ * allows to sensitize or influence visibility of all the actions in it
+ * by setting sensitive or visible property of the group itself.
+ *
+ * The #EUIActionGroup can serve also as a #GActionMap.
+ *
+ * The object is not thread-safe, it's meant to be used only from
+ * the main/GUI thread.
+ *
+ * Since: 3.56
+ **/
+
+struct _EUIActionGroup {
+ GSimpleActionGroup parent;
+
+ gchar *name;
+ GHashTable *items; /* gchar *name ~> EUIAction * (owned) */
+ gboolean sensitive;
+ gboolean visible;
+};
+
+G_DEFINE_TYPE (EUIActionGroup, e_ui_action_group, G_TYPE_SIMPLE_ACTION_GROUP)
+
+enum {
+ PROP_0,
+ PROP_NAME,
+ PROP_SENSITIVE,
+ PROP_VISIBLE,
+ N_PROPS
+};
+
+enum {
+ SIGNAL_ADDED,
+ SIGNAL_REMOVED,
+ SIGNAL_ACCEL_ADDED,
+ SIGNAL_ACCEL_REMOVED,
+ N_SIGNALS
+};
+
+static guint signals[N_SIGNALS] = { 0, };
+static GParamSpec *properties[N_PROPS] = { NULL, };
+
+static void
+e_ui_action_group_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EUIActionGroup *self = E_UI_ACTION_GROUP (object);
+
+ switch (prop_id) {
+ case PROP_NAME:
+ g_free (self->name);
+ self->name = g_value_dup_string (value);
+ break;
+ case PROP_SENSITIVE:
+ e_ui_action_group_set_sensitive (self, g_value_get_boolean (value));
+ break;
+ case PROP_VISIBLE:
+ e_ui_action_group_set_visible (self, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+e_ui_action_group_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EUIActionGroup *self = E_UI_ACTION_GROUP (object);
+
+ switch (prop_id) {
+ case PROP_NAME:
+ g_value_set_string (value, e_ui_action_group_get_name (self));
+ break;
+ case PROP_SENSITIVE:
+ g_value_set_boolean (value, e_ui_action_group_get_sensitive (self));
+ break;
+ case PROP_VISIBLE:
+ g_value_set_boolean (value, e_ui_action_group_get_visible (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+e_ui_action_group_dispose (GObject *object)
+{
+ EUIActionGroup *self = E_UI_ACTION_GROUP (object);
+
+ e_ui_action_group_remove_all (self);
+
+ G_OBJECT_CLASS (e_ui_action_group_parent_class)->dispose (object);
+}
+
+static void
+e_ui_action_group_finalize (GObject *object)
+{
+ EUIActionGroup *self = E_UI_ACTION_GROUP (object);
+
+ g_clear_pointer (&self->name, g_free);
+ g_clear_pointer (&self->items, g_hash_table_unref);
+
+ G_OBJECT_CLASS (e_ui_action_group_parent_class)->finalize (object);
+}
+
+static void
+e_ui_action_group_class_init (EUIActionGroupClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->set_property = e_ui_action_group_set_property;
+ object_class->get_property = e_ui_action_group_get_property;
+ object_class->dispose = e_ui_action_group_dispose;
+ object_class->finalize = e_ui_action_group_finalize;
+
+ /**
+ * EUIActionGroup:name:
+ *
+ * Name of the action group.
+ *
+ * Since: 3.56
+ **/
+ properties[PROP_NAME] = g_param_spec_string ("name", NULL, NULL, NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * EUIActionGroup:sensitive:
+ *
+ * Get or set whether the action group is sensitive, which influences
+ * all the actions in the group.
+ *
+ * Since: 3.56
+ **/
+ properties[PROP_SENSITIVE] = g_param_spec_boolean ("sensitive", NULL, NULL, TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+
+ /**
+ * EUIActionGroup:visible:
+ *
+ * Get or set whether the action group is visible, which influences
+ * all the actions in the group.
+ *
+ * Since: 3.56
+ **/
+ properties[PROP_VISIBLE] = g_param_spec_boolean ("visible", NULL, NULL, TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
+
+ /* void added (EUIActionGroup *action_group,
+ EUIAction *action); */
+ /**
+ * EUIActionGroup::added:
+ * @action_group: an #EUIActionGroup
+ * @action: an #EUIAction, which had been just added
+ *
+ * A signal emitted when the @action_group added @action into itself.
+ *
+ * Since: 3.56
+ **/
+ signals[SIGNAL_ADDED] = g_signal_new ("added",
+ E_TYPE_UI_ACTION_GROUP,
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ 0,
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 1,
+ E_TYPE_UI_ACTION);
+
+ /* void removed (EUIActionGroup *action_group,
+ EUIAction *action); */
+ /**
+ * EUIActionGroup::removed:
+ * @action_group: an #EUIActionGroup
+ * @action: an #EUIAction, which had been just removed
+ *
+ * A signal emitted when the @action_group removed @action from itself.
+ *
+ * Since: 3.56
+ **/
+ signals[SIGNAL_REMOVED] = g_signal_new ("removed",
+ E_TYPE_UI_ACTION_GROUP,
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ 0,
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 1,
+ E_TYPE_UI_ACTION);
+
+ /* void accel_added (EUIActionGroup *action_group,
+ EUIAction *action,
+ const gchar *accel); */
+ /**
+ * EUIActionGroup::accel-added:
+ * @action_group: an #EUIActionGroup
+ * @action: an #EUIAction, whose accel was added
+ * @accel: the added accel
+ *
+ * A signal emitted when one of the @action_group @action has added
+ * an accelerator string.
+ *
+ * Since: 3.56
+ **/
+ signals[SIGNAL_ACCEL_ADDED] = g_signal_new ("accel-added",
+ E_TYPE_UI_ACTION_GROUP,
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ 0,
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 2,
+ E_TYPE_UI_ACTION,
+ G_TYPE_STRING);
+
+ /* void accel_removed (EUIActionGroup *action_group,
+ EUIAction *action,
+ const gchar *accel); */
+ /**
+ * EUIActionGroup::accel-removed:
+ * @action_group: an #EUIActionGroup
+ * @action: an #EUIAction, whose accel was removed
+ * @accel: the removed accel
+ *
+ * A signal emitted when one of the @action_group @action has removed
+ * an accelerator string.
+ *
+ * Since: 3.56
+ **/
+ signals[SIGNAL_ACCEL_REMOVED] = g_signal_new ("accel-removed",
+ E_TYPE_UI_ACTION_GROUP,
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ 0,
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 2,
+ E_TYPE_UI_ACTION,
+ G_TYPE_STRING);
+}
+
+static void
+e_ui_action_group_init (EUIActionGroup *self)
+{
+ self->items = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
+ self->sensitive = TRUE;
+ self->visible = TRUE;
+}
+
+/**
+ * e_ui_action_group_new:
+ * @name: (not nullable): a group name
+ *
+ * Creates a new #EUIActionGroup named @name.
+ *
+ * Returns: (transfer full): a new #EUIActionGroup
+ *
+ * Since: 3.56
+ **/
+EUIActionGroup *
+e_ui_action_group_new (const gchar *name)
+{
+ g_return_val_if_fail (name != NULL, NULL);
+
+ return g_object_new (E_TYPE_UI_ACTION_GROUP,
+ "name", name,
+ NULL);
+}
+
+/**
+ * e_ui_action_group_get_name:
+ * @self: an #EUIActionGroup
+ *
+ * Get a name of the @self.
+ *
+ * Returns: a name of the @self
+ *
+ * Since: 3.56
+ **/
+const gchar *
+e_ui_action_group_get_name (EUIActionGroup *self)
+{
+ g_return_val_if_fail (E_IS_UI_ACTION_GROUP (self), NULL);
+
+ return self->name;
+}
+
+static void
+e_ui_action_group_action_accel_added_cb (EUIAction *action,
+ const gchar *accel,
+ gpointer user_data)
+{
+ EUIActionGroup *self = user_data;
+
+ g_signal_emit (self, signals[SIGNAL_ACCEL_ADDED], 0, action, accel, NULL);
+}
+
+static void
+e_ui_action_group_action_accel_removed_cb (EUIAction *action,
+ const gchar *accel,
+ gpointer user_data)
+{
+ EUIActionGroup *self = user_data;
+
+ g_signal_emit (self, signals[SIGNAL_ACCEL_REMOVED], 0, action, accel, NULL);
+}
+
+/**
+ * e_ui_action_group_add:
+ * @self: an #EUIActionGroup
+ * @action: (transfer none): an #EUIAction
+ *
+ * Adds an action into the @self. When the @action is already in this group,
+ * it does nothing. When the @action is part of another group, it's removed
+ * from it first. In other words, the @action can be part of a single group
+ * only.
+ *
+ * Adding a new action of the same name as another action already in the group
+ * is considered as a programming error.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_action_group_add (EUIActionGroup *self,
+ EUIAction *action)
+{
+ EUIAction *adept;
+ const gchar *name;
+
+ g_return_if_fail (E_IS_UI_ACTION_GROUP (self));
+ g_return_if_fail (E_IS_UI_ACTION (action));
+
+ name = g_action_get_name (G_ACTION (action));
+ adept = g_hash_table_lookup (self->items, name);
+
+ if (adept == action)
+ return;
+
+ if (adept) {
+ g_warning ("%s: Other action of the name '%s' is in the group, skipping", G_STRFUNC, name);
+ return;
+ }
+
+ if (!e_ui_action_get_label (action))
+ g_warning ("%s: Action '%s' does not have set label", G_STRFUNC, name);
+
+ g_hash_table_insert (self->items, (gpointer) name, g_object_ref (action));
+ g_action_map_add_action (G_ACTION_MAP (self), G_ACTION (action));
+ e_ui_action_set_action_group (action, self);
+
+ g_signal_connect_object (action, "accel-added",
+ G_CALLBACK (e_ui_action_group_action_accel_added_cb), self, 0);
+ g_signal_connect_object (action, "accel-removed",
+ G_CALLBACK (e_ui_action_group_action_accel_removed_cb), self, 0);
+
+ g_signal_emit (self, signals[SIGNAL_ADDED], 0, action, NULL);
+}
+
+/**
+ * e_ui_action_group_remove:
+ * @self: an #EUIActionGroup
+ * @action: an #EUIAction
+ *
+ * Removes @action from the @self. It does nothing when the action
+ * does not exist in the group.
+ *
+ * Trying to remove an action of the same name as another action already
+ * in the group is considered as a programming error.
+ *
+ * See e_ui_action_group_remove_by_name().
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_action_group_remove (EUIActionGroup *self,
+ EUIAction *action)
+{
+ EUIAction *adept;
+ const gchar *name;
+
+ g_return_if_fail (E_IS_UI_ACTION_GROUP (self));
+ g_return_if_fail (E_IS_UI_ACTION (action));
+
+ name = g_action_get_name (G_ACTION (action));
+ adept = g_hash_table_lookup (self->items, name);
+
+ if (adept != action) {
+ if (adept)
+ g_warning ("%s: Other action of the name '%s' is in the group, skipping", G_STRFUNC, name);
+ return;
+ }
+
+ g_object_ref (action);
+
+ g_hash_table_remove (self->items, (gpointer) name);
+ e_ui_action_set_action_group (action, NULL);
+ g_action_map_remove_action (G_ACTION_MAP (self), name);
+
+ g_signal_handlers_disconnect_by_func (action, G_CALLBACK (e_ui_action_group_action_accel_added_cb), self);
+ g_signal_handlers_disconnect_by_func (action, G_CALLBACK (e_ui_action_group_action_accel_removed_cb), self);
+
+ g_signal_emit (self, signals[SIGNAL_REMOVED], 0, action, NULL);
+
+ g_object_unref (action);
+}
+
+/**
+ * e_ui_action_group_remove_by_name:
+ * @self: an #EUIActionGroup
+ * @action_name: (not nullable): an action name
+ *
+ * Removes an action from the @self identified by the @action_name.
+ * It does nothing, when the group does not contain any such
+ * named action.
+ *
+ * See e_ui_action_group_remove_by_name().
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_action_group_remove_by_name (EUIActionGroup *self,
+ const gchar *action_name)
+{
+ EUIAction *action;
+
+ g_return_if_fail (E_IS_UI_ACTION_GROUP (self));
+ g_return_if_fail (action_name != NULL);
+
+ action = g_hash_table_lookup (self->items, action_name);
+ if (!action)
+ return;
+
+ e_ui_action_group_remove (self, action);
+}
+
+/**
+ * e_ui_action_group_remove_all:
+ * @self: an #EUIActionGroup
+ *
+ * Removes all actions from the @self.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_action_group_remove_all (EUIActionGroup *self)
+{
+ GPtrArray *actions;
+ GActionMap *action_map;
+ GHashTableIter iter;
+ gpointer value = NULL;
+ guint ii;
+
+ g_return_if_fail (E_IS_UI_ACTION_GROUP (self));
+
+ if (!g_hash_table_size (self->items))
+ return;
+
+ actions = g_ptr_array_new_full (g_hash_table_size (self->items), g_object_unref);
+
+ g_hash_table_iter_init (&iter, self->items);
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
+ EUIAction *action = value;
+ g_ptr_array_add (actions, g_object_ref (action));
+ }
+
+ g_hash_table_remove_all (self->items);
+
+ action_map = G_ACTION_MAP (self);
+
+ for (ii = 0; ii < actions->len; ii++) {
+ EUIAction *action = g_ptr_array_index (actions, ii);
+ e_ui_action_set_action_group (action, NULL);
+ g_action_map_remove_action (action_map, g_action_get_name (G_ACTION (action)));
+ g_signal_handlers_disconnect_by_func (action, G_CALLBACK (e_ui_action_group_action_accel_added_cb), self);
+ g_signal_handlers_disconnect_by_func (action, G_CALLBACK (e_ui_action_group_action_accel_removed_cb), self);
+ g_signal_emit (self, signals[SIGNAL_REMOVED], 0, action, NULL);
+ }
+
+ g_ptr_array_unref (actions);
+}
+
+/**
+ * e_ui_action_group_get_action:
+ * @self: an #EUIActionGroup
+ * @action_name: (not nullable): an action name
+ *
+ * Looks up an action by its name in the @self.
+ *
+ * Returns: (transfer none) (nullable): an #EUIAction of the name @action_name,
+ * or %NULL, when no such named action exists in the group
+ *
+ * Since: 3.56
+ **/
+EUIAction *
+e_ui_action_group_get_action (EUIActionGroup *self,
+ const gchar *action_name)
+{
+ g_return_val_if_fail (E_IS_UI_ACTION_GROUP (self), NULL);
+ g_return_val_if_fail (action_name != NULL, NULL);
+
+ return g_hash_table_lookup (self->items, action_name);
+}
+
+/**
+ * e_ui_action_group_list_actions:
+ * @self: an #EUIActionGroup
+ *
+ * List all the actions in the @self. Free the returned #GPtrArray
+ * with g_ptr_array_unref(), when no longer needed.
+ *
+ * Returns: (transfer container) (element-type EUIAction): all actions in the @self
+ *
+ * Since: 3.56
+ **/
+GPtrArray *
+e_ui_action_group_list_actions (EUIActionGroup *self)
+{
+ GPtrArray *actions;
+ GHashTableIter iter;
+ gpointer value = NULL;
+
+ g_return_val_if_fail (E_IS_UI_ACTION_GROUP (self), NULL);
+
+ actions = g_ptr_array_new_full (g_hash_table_size (self->items), g_object_unref);
+
+ g_hash_table_iter_init (&iter, self->items);
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
+ EUIAction *action = value;
+ g_ptr_array_add (actions, g_object_ref (action));
+ }
+
+ return actions;
+}
+
+/**
+ * e_ui_action_group_get_visible:
+ * @self: an #EUIActionGroup
+ *
+ * Checks whether the group, and also all the actions in the group, can be visible.
+ *
+ * Returns: whether the group and all its actions can be visible
+ *
+ * Since: 3.56
+ **/
+gboolean
+e_ui_action_group_get_visible (EUIActionGroup *self)
+{
+ g_return_val_if_fail (E_IS_UI_ACTION_GROUP (self), FALSE);
+
+ return self->visible;
+}
+
+/**
+ * e_ui_action_group_set_visible:
+ * @self: an #EUIActionGroup
+ * @value: value to set
+ *
+ * Sets whether the group, and also all the actions in the group, can be visible.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_action_group_set_visible (EUIActionGroup *self,
+ gboolean value)
+{
+ GHashTableIter iter;
+ gpointer ht_value = NULL;
+
+ g_return_if_fail (E_IS_UI_ACTION_GROUP (self));
+
+ if ((!self->visible) == (!value))
+ return;
+
+ self->visible = value;
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VISIBLE]);
+
+ g_hash_table_iter_init (&iter, self->items);
+ while (g_hash_table_iter_next (&iter, NULL, &ht_value)) {
+ GObject *action = ht_value;
+ g_object_notify (action, "is-visible");
+ }
+}
+
+/**
+ * e_ui_action_group_get_sensitive:
+ * @self: an #EUIActionGroup
+ *
+ * Checks whether the group, and also all the actions in the group, can be sensitive.
+ *
+ * Returns: whether the group and all its actions can be sensitive
+ *
+ * Since: 3.56
+ **/
+gboolean
+e_ui_action_group_get_sensitive (EUIActionGroup *self)
+{
+ g_return_val_if_fail (E_IS_UI_ACTION_GROUP (self), FALSE);
+
+ return self->sensitive;
+}
+
+/**
+ * e_ui_action_group_set_sensitive:
+ * @self: an #EUIActionGroup
+ * @value: a value to set
+ *
+ * Sets whether the group, and also all the actions in the group, can be visible.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_action_group_set_sensitive (EUIActionGroup *self,
+ gboolean value)
+{
+ GHashTableIter iter;
+ gpointer ht_value = NULL;
+
+ g_return_if_fail (E_IS_UI_ACTION_GROUP (self));
+
+ if ((!self->sensitive) == (!value))
+ return;
+
+ self->sensitive = value;
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SENSITIVE]);
+
+ g_hash_table_iter_init (&iter, self->items);
+ while (g_hash_table_iter_next (&iter, NULL, &ht_value)) {
+ GObject *action = ht_value;
+ g_object_notify (action, "enabled");
+ }
+}
diff --git a/src/e-util/e-ui-action-group.h b/src/e-util/e-ui-action-group.h
new file mode 100644
index 0000000000..230c61898b
--- /dev/null
+++ b/src/e-util/e-ui-action-group.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * SPDX-FileCopyrightText: (C) 2024 Red Hat (www.redhat.com>
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only should be included directly."
+#endif
+
+#ifndef E_UI_ACTION_GROUP_H
+#define E_UI_ACTION_GROUP_H
+
+#include
+#include
+
+G_BEGIN_DECLS
+
+#define E_TYPE_UI_ACTION_GROUP e_ui_action_group_get_type ()
+
+G_DECLARE_FINAL_TYPE (EUIActionGroup, e_ui_action_group, E, UI_ACTION_GROUP, GSimpleActionGroup)
+
+EUIActionGroup *e_ui_action_group_new (const gchar *name);
+const gchar * e_ui_action_group_get_name (EUIActionGroup *self);
+void e_ui_action_group_add (EUIActionGroup *self,
+ EUIAction *action);
+void e_ui_action_group_remove (EUIActionGroup *self,
+ EUIAction *action);
+void e_ui_action_group_remove_by_name(EUIActionGroup *self,
+ const gchar *action_name);
+void e_ui_action_group_remove_all (EUIActionGroup *self);
+EUIAction * e_ui_action_group_get_action (EUIActionGroup *self,
+ const gchar *action_name);
+GPtrArray * e_ui_action_group_list_actions (EUIActionGroup *self);
+gboolean e_ui_action_group_get_visible (EUIActionGroup *self);
+void e_ui_action_group_set_visible (EUIActionGroup *self,
+ gboolean value);
+gboolean e_ui_action_group_get_sensitive (EUIActionGroup *self);
+void e_ui_action_group_set_sensitive (EUIActionGroup *self,
+ gboolean value);
+
+G_END_DECLS
+
+#endif /* E_UI_ACTION_GROUP_H */
diff --git a/src/e-util/e-ui-action.c b/src/e-util/e-ui-action.c
new file mode 100644
index 0000000000..0c3d8f8037
--- /dev/null
+++ b/src/e-util/e-ui-action.c
@@ -0,0 +1,1757 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * SPDX-FileCopyrightText: (C) 2024 Red Hat (www.redhat.com>
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "evolution-config.h"
+
+#include
+#include
+#include
+
+#include "e-headerbar-button.h"
+#include "e-ui-action-group.h"
+
+#include "e-ui-action.h"
+
+/**
+ * SECTION: e-ui-action
+ * @include: e-util/e-util.h
+ * @short_description: a UI action
+ *
+ * The #EUIAction is similar to a #GSimpleAction, with added properties
+ * suitable for easier manipulation of the action appearance. It implements
+ * a #GAction interface.
+ *
+ * The action can be part of a single #EUIActionGroup only. The GAction::enabled
+ * property considers also the groups sensitive value.
+ *
+ * While the @EUIAction:visible property is relevant for the action itself,
+ * the @EUIAction:is-visible property considers also visibility of the group, if any.
+ *
+ * The object is not thread-safe, it's meant to be used only from
+ * the main/GUI thread.
+ *
+ * Since: 3.56
+ **/
+
+/**
+ * EUIActionFunc:
+ * @action: an #EUIAction
+ * @value: (nullable): a #GVariant value for the function
+ * @user_data: user data for the function
+ *
+ * A function called either on @action activate or change_state signal.
+ *
+ * For the activate, the @value is a parameter, with which the activate
+ * was called. Stateless actions do not provide any parameter.
+ *
+ * For the change_state, the @value is a new value the state should be changed to.
+ *
+ * Since: 3.56
+ **/
+
+/**
+ * EUIActionEntry:
+ * @name: an action name
+ * @icon_name: an icon name
+ * @label: an action label
+ * @accel: an action accelerator
+ * @tooltip: an action tooltip
+ * @activate: (nullable): an #EUIActionFunc callback to call when the action is activated, or %NULL
+ * @parameter_type: an action parameter type
+ * @state: an action state for stateful actions
+ * @change_state: (nullable): an #EUIActionFunc callback to call when the state of the action should be changed, or %NULL
+ *
+ * A structure describing an action, which can be created in bulk
+ * with e_ui_manager_add_actions() function.
+ *
+ * Simple actions declare only the @activate callback, where it reacts
+ * to the action activation.
+ *
+ * To create a checkbox-like widgets from the action, set the @state
+ * to a boolean value (like "true") and provide a @change_state
+ * function, which will set e_ui_action_set_state (action, value);
+ * and also react to the change of the state.
+ *
+ * To create a radiobutton-like widgets from the action, set the @parameter_type
+ * for example to string ("s"), the @state to the value the radio group
+ * should be switched to (like "'value1'") and the @change_state
+ * function, which calls e_ui_action_set_state (action, value); and reacts
+ * to the change of the state. Each action in the radio group can use the same
+ * @change_state callback. Make sure the actions are part of the same radio
+ * group with the e_ui_action_set_radio_group().
+ *
+ * See #EUIActionEnumEntry
+ *
+ * Since: 3.56
+ **/
+
+/**
+ * EUIActionEnumEntry:
+ * @name: an action name
+ * @icon_name: an icon name
+ * @label: an action label
+ * @accel: an action accelerator
+ * @tooltip: an action tooltip
+ * @activate: (nullable): an #EUIActionFunc callback to call when the action is activated, or %NULL
+ * @state: the corresponding enum value, as gint
+ *
+ * A structure describing a radio action, whose states are enum values.
+ * The actions can be created in bulk with e_ui_manager_add_actions_enum() function.
+ *
+ * See #EUIActionEntry.
+ *
+ * Since: 3.56
+ **/
+
+/* This is a copy of the GSimpleAction, with added "visible" property */
+
+struct _EUIAction {
+ GObject parent;
+
+ gchar *map_name;
+ gchar *name;
+ gchar *icon_name;
+ gchar *label;
+ gchar *accel;
+ gchar *tooltip;
+ GVariantType *parameter_type;
+ GVariant *target; /* the first state set; needed for radio groups */
+ GVariant *state;
+ GVariant *state_hint;
+ GPtrArray *secondary_accels;
+ GPtrArray *radio_group;
+ EUIActionGroup *action_group;
+ gboolean sensitive;
+ gboolean visible;
+};
+
+static void e_ui_action_action_iface_init (GActionInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (EUIAction, e_ui_action, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_ACTION, e_ui_action_action_iface_init))
+
+enum {
+ PROP_0,
+ PROP_MAP_NAME,
+ PROP_NAME,
+ PROP_ICON_NAME,
+ PROP_LABEL,
+ PROP_ACCEL,
+ PROP_TOOLTIP,
+ PROP_PARAMETER_TYPE,
+ PROP_ENABLED,
+ PROP_STATE_TYPE,
+ PROP_STATE,
+ PROP_STATE_HINT,
+ PROP_VISIBLE,
+ PROP_SENSITIVE,
+ PROP_IS_VISIBLE,
+ PROP_ACTIVE,
+ N_PROPS
+};
+
+enum {
+ SIGNAL_CHANGE_STATE,
+ SIGNAL_ACTIVATE,
+ SIGNAL_CHANGED,
+ SIGNAL_ACCEL_ADDED,
+ SIGNAL_ACCEL_REMOVED,
+ N_SIGNALS
+};
+
+static GParamSpec *properties[N_PROPS] = { NULL, };
+static guint signals[N_SIGNALS] = { 0, };
+
+static const gchar *
+e_ui_action_get_name (GAction *action)
+{
+ g_return_val_if_fail (E_IS_UI_ACTION (action), NULL);
+
+ return E_UI_ACTION (action)->name;
+}
+
+static const GVariantType *
+e_ui_action_get_parameter_type (GAction *action)
+{
+ g_return_val_if_fail (E_IS_UI_ACTION (action), NULL);
+
+ return E_UI_ACTION (action)->parameter_type;
+}
+
+static GVariant *
+e_ui_action_get_state (GAction *action)
+{
+ EUIAction *self;
+
+ g_return_val_if_fail (E_IS_UI_ACTION (action), NULL);
+
+ self = E_UI_ACTION (action);
+
+ return self->state ? g_variant_ref (self->state) : NULL;
+}
+
+static const GVariantType *
+e_ui_action_get_state_type (GAction *action)
+{
+ EUIAction *self;
+
+ g_return_val_if_fail (E_IS_UI_ACTION (action), NULL);
+
+ self = E_UI_ACTION (action);
+
+ return self->state ? g_variant_get_type (self->state) : NULL;
+}
+
+static GVariant *
+e_ui_action_get_state_hint (GAction *action)
+{
+ EUIAction *self;
+
+ g_return_val_if_fail (E_IS_UI_ACTION (action), NULL);
+
+ self = E_UI_ACTION (action);
+
+ return self->state_hint ? g_variant_ref (self->state_hint) : NULL;
+}
+
+static gboolean
+e_ui_action_get_enabled (GAction *action)
+{
+ EUIAction *self;
+
+ g_return_val_if_fail (E_IS_UI_ACTION (action), FALSE);
+
+ self = E_UI_ACTION (action);
+
+ return self->sensitive && (!self->action_group || e_ui_action_group_get_sensitive (self->action_group));
+}
+
+static void
+e_ui_action_change_state (GAction *action,
+ GVariant *value)
+{
+ EUIAction *self = E_UI_ACTION (action);
+
+ /* If the user connected a signal handler then they are responsible
+ * for handling state changes.
+ */
+ if (g_signal_has_handler_pending (self, signals[SIGNAL_CHANGE_STATE], 0, TRUE))
+ g_signal_emit (self, signals[SIGNAL_CHANGE_STATE], 0, value);
+
+ /* If not, then the default behaviour is to just set the state. */
+ else
+ e_ui_action_set_state (self, value);
+}
+
+static void
+e_ui_action_activate (GAction *action,
+ GVariant *parameter)
+{
+ EUIAction *self = E_UI_ACTION (action);
+
+ g_return_if_fail (self->parameter_type == NULL ? parameter == NULL :
+ (parameter != NULL && g_variant_is_of_type (parameter, self->parameter_type)));
+
+ if (g_action_get_enabled (action) && e_ui_action_is_visible (self)) {
+ if (parameter != NULL)
+ g_variant_ref_sink (parameter);
+
+ /* If the user connected a signal handler then they are responsible
+ * for handling activation.
+ */
+ if (g_signal_has_handler_pending (self, signals[SIGNAL_ACTIVATE], 0, TRUE)) {
+ g_signal_emit (self, signals[SIGNAL_ACTIVATE], 0, parameter);
+
+ /* If not, do some reasonable defaults for stateful actions. */
+ } else if (self->state) {
+ /* If we have no parameter and this is a boolean action, toggle. */
+ if (parameter == NULL && g_variant_is_of_type (self->state, G_VARIANT_TYPE_BOOLEAN)) {
+ gboolean was_enabled = g_variant_get_boolean (self->state);
+ e_ui_action_change_state (action, g_variant_new_boolean (!was_enabled));
+ /* else, if the parameter and state type are the same, do a change-state */
+ } else if (g_variant_is_of_type (self->state, g_variant_get_type (parameter))) {
+ e_ui_action_change_state (action, parameter);
+ }
+ }
+
+ if (parameter != NULL)
+ g_variant_unref (parameter);
+ }
+}
+
+static void
+e_ui_action_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EUIAction *self = E_UI_ACTION (object);
+
+ switch (prop_id) {
+ case PROP_MAP_NAME:
+ g_free (self->map_name);
+ self->map_name = g_value_dup_string (value);
+ break;
+ case PROP_NAME:
+ g_free (self->name);
+ self->name = g_value_dup_string (value);
+ break;
+ case PROP_ICON_NAME:
+ e_ui_action_set_icon_name (self, g_value_get_string (value));
+ break;
+ case PROP_LABEL:
+ e_ui_action_set_label (self, g_value_get_string (value));
+ break;
+ case PROP_ACCEL:
+ e_ui_action_set_accel (self, g_value_get_string (value));
+ break;
+ case PROP_TOOLTIP:
+ e_ui_action_set_tooltip (self, g_value_get_string (value));
+ break;
+ case PROP_PARAMETER_TYPE:
+ g_clear_pointer (&self->parameter_type, g_variant_type_free);
+ self->parameter_type = g_value_dup_boxed (value);
+ break;
+ case PROP_ENABLED:
+ e_ui_action_set_sensitive (self, g_value_get_boolean (value));
+ break;
+ case PROP_STATE:
+ e_ui_action_set_state (self, g_value_get_variant (value));
+ break;
+ case PROP_STATE_HINT:
+ e_ui_action_set_state_hint (self, g_value_get_variant (value));
+ break;
+ case PROP_VISIBLE:
+ e_ui_action_set_visible (self, g_value_get_boolean (value));
+ break;
+ case PROP_SENSITIVE:
+ e_ui_action_set_sensitive (self, g_value_get_boolean (value));
+ break;
+ case PROP_ACTIVE:
+ e_ui_action_set_active (self, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+e_ui_action_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GAction *action = G_ACTION (object);
+
+ switch (prop_id) {
+ case PROP_MAP_NAME:
+ g_value_set_string (value, e_ui_action_get_map_name (E_UI_ACTION (action)));
+ break;
+ case PROP_NAME:
+ g_value_set_string (value, e_ui_action_get_name (action));
+ break;
+ case PROP_ICON_NAME:
+ g_value_set_string (value, e_ui_action_get_icon_name (E_UI_ACTION (action)));
+ break;
+ case PROP_LABEL:
+ g_value_set_string (value, e_ui_action_get_label (E_UI_ACTION (action)));
+ break;
+ case PROP_ACCEL:
+ g_value_set_string (value, e_ui_action_get_accel (E_UI_ACTION (action)));
+ break;
+ case PROP_TOOLTIP:
+ g_value_set_string (value, e_ui_action_get_tooltip (E_UI_ACTION (action)));
+ break;
+ case PROP_PARAMETER_TYPE:
+ g_value_set_boxed (value, e_ui_action_get_parameter_type (action));
+ break;
+ case PROP_ENABLED:
+ g_value_set_boolean (value, e_ui_action_get_enabled (action));
+ break;
+ case PROP_STATE_TYPE:
+ g_value_set_boxed (value, e_ui_action_get_state_type (action));
+ break;
+ case PROP_STATE:
+ g_value_take_variant (value, e_ui_action_get_state (action));
+ break;
+ case PROP_STATE_HINT:
+ g_value_take_variant (value, e_ui_action_get_state_hint (action));
+ break;
+ case PROP_VISIBLE:
+ g_value_set_boolean (value, e_ui_action_get_visible (E_UI_ACTION (action)));
+ break;
+ case PROP_SENSITIVE:
+ g_value_set_boolean (value, e_ui_action_get_sensitive (E_UI_ACTION (action)));
+ break;
+ case PROP_ACTIVE:
+ g_value_set_boolean (value, e_ui_action_get_active (E_UI_ACTION (action)));
+ break;
+ case PROP_IS_VISIBLE:
+ g_value_set_boolean (value, e_ui_action_is_visible (E_UI_ACTION (action)));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+e_ui_action_finalize (GObject *object)
+{
+ EUIAction *self = E_UI_ACTION (object);
+
+ e_ui_action_set_radio_group (self, NULL);
+ e_ui_action_set_action_group (self, NULL);
+
+ g_clear_pointer (&self->map_name, g_free);
+ g_clear_pointer (&self->name, g_free);
+ g_clear_pointer (&self->icon_name, g_free);
+ g_clear_pointer (&self->label, g_free);
+ g_clear_pointer (&self->accel, g_free);
+ g_clear_pointer (&self->tooltip, g_free);
+ g_clear_pointer (&self->secondary_accels, g_ptr_array_unref);
+ g_clear_pointer (&self->parameter_type, g_variant_type_free);
+ g_clear_pointer (&self->target, g_variant_unref);
+ g_clear_pointer (&self->state, g_variant_unref);
+ g_clear_pointer (&self->state_hint, g_variant_unref);
+
+ G_OBJECT_CLASS (e_ui_action_parent_class)->finalize (object);
+}
+
+static void
+e_ui_action_action_iface_init (GActionInterface *iface)
+{
+ iface->get_name = e_ui_action_get_name;
+ iface->get_parameter_type = e_ui_action_get_parameter_type;
+ iface->get_state_type = e_ui_action_get_state_type;
+ iface->get_state_hint = e_ui_action_get_state_hint;
+ iface->get_enabled = e_ui_action_get_enabled;
+ iface->get_state = e_ui_action_get_state;
+ iface->change_state = e_ui_action_change_state;
+ iface->activate = e_ui_action_activate;
+}
+
+static void
+e_ui_action_class_init (EUIActionClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->set_property = e_ui_action_set_property;
+ object_class->get_property = e_ui_action_get_property;
+ object_class->finalize = e_ui_action_finalize;
+
+ /**
+ * EUIAction:map-name:
+ *
+ * A #GActionMap name the action is declared at. It usually matches #EUIActionGroup
+ * name, when one is set on the action.
+ *
+ * Since: 3.56
+ **/
+ properties[PROP_MAP_NAME] = g_param_spec_string ("map-name", NULL, NULL, NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * EUIAction:name:
+ *
+ * An action name.
+ *
+ * Since: 3.56
+ **/
+ properties[PROP_NAME] = g_param_spec_string ("name", NULL, NULL, NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * EUIAction:icon-name:
+ *
+ * An icon name, which can be a standard icon name, or a text with prefix "gicon::",
+ * which are generated by the action owner through #EUIManager::create-gicon signal.
+ * The signal is executed without the "gicon::" prefix.
+ *
+ * Since: 3.56
+ **/
+ properties[PROP_ICON_NAME] = g_param_spec_string ("icon-name", NULL, NULL, NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * EUIAction:label:
+ *
+ * An action label.
+ *
+ * Since: 3.56
+ **/
+ properties[PROP_LABEL] = g_param_spec_string ("label", NULL, NULL, NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * EUIAction:accel:
+ *
+ * An action accelerator.
+ *
+ * Since: 3.56
+ **/
+ properties[PROP_ACCEL] = g_param_spec_string ("accel", NULL, NULL, NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * EUIAction:tooltip:
+ *
+ * An action tooltip text (not markup).
+ *
+ * Since: 3.56
+ **/
+ properties[PROP_TOOLTIP] = g_param_spec_string ("tooltip", NULL, NULL, NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * EUIAction:parameter-type:
+ *
+ * A parameter type of a stateful action, as a #GVariantType, or %NULL.
+ *
+ * Since: 3.56
+ **/
+ properties[PROP_PARAMETER_TYPE] = g_param_spec_boxed ("parameter-type", NULL, NULL, G_TYPE_VARIANT_TYPE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * EUIAction:enabled:
+ *
+ * An enabled state of the action. When the action is part of an action group,
+ * the @EUIActionGroup:sensitive is consulted together with the @EUIAction:sensitive
+ * property when determining whether the action is enabled.
+ *
+ * Since: 3.56
+ **/
+ properties[PROP_ENABLED] = g_param_spec_boolean ("enabled", NULL, NULL, TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * EUIAction:state-type:
+ *
+ * State type of a stateful action, as a #GVariantType, or %NULL.
+ *
+ * Since: 3.56
+ **/
+ properties[PROP_STATE_TYPE] = g_param_spec_boxed ("state-type", NULL, NULL, G_TYPE_VARIANT_TYPE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * EUIAction:state:
+ *
+ * A state of a stateful action, as a #GVariant, or %NULL.
+ *
+ * Since: 3.56
+ **/
+ properties[PROP_STATE] = g_param_spec_variant ("state", NULL, NULL, G_VARIANT_TYPE_ANY, NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * EUIAction:state-hint:
+ *
+ * A state hint of a stateful action, as a #GVariantType, or %NULL.
+ *
+ * Since: 3.56
+ **/
+ properties[PROP_STATE_HINT] = g_param_spec_boxed ("state-hint", NULL, NULL, G_TYPE_VARIANT_TYPE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * EUIAction:visible:
+ *
+ * Whether the action is visible. It does not consult associated
+ * #EUIActionGroup visibility.
+ *
+ * Since: 3.56
+ **/
+ properties[PROP_VISIBLE] = g_param_spec_boolean ("visible", NULL, NULL, TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * EUIAction:is-visible:
+ *
+ * Read-only property to determine whether both @EUIAction:visible property and
+ * possibly associated #EUIActionGroup 's visible property are %TRUE.
+ *
+ * Since: 3.56
+ **/
+ properties[PROP_IS_VISIBLE] = g_param_spec_boolean ("is-visible", NULL, NULL, TRUE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * EUIAction:sensitive:
+ *
+ * Action's sensitive state. It does not consult associated
+ * #EUIActionGroup sensitivity.
+ *
+ * Since: 3.56
+ **/
+ properties[PROP_SENSITIVE] = g_param_spec_boolean ("sensitive", NULL, NULL, TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * EUIAction:active:
+ *
+ * Action's active state. It is relevant only for stateful actions, like toggle
+ * actions (with a boolean state) or radio actions (with state and target).
+ * See e_ui_action_get_active() and e_ui_action_set_active() for more information.
+ *
+ * Since: 3.56
+ **/
+ properties[PROP_ACTIVE] = g_param_spec_boolean ("active", NULL, NULL, TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
+
+ /**
+ * EUIAction::activate:
+ * @self: an #EUIAction
+ * @parameter: (nullable): an activation parameter as a #GVariant, or %NULL
+ *
+ * A signal emitted when the action should be activated.
+ * The @parameter is provided only for stateful actions.
+ *
+ * Since: 3.56
+ **/
+ signals[SIGNAL_ACTIVATE] = g_signal_new ("activate",
+ E_TYPE_UI_ACTION,
+ G_SIGNAL_RUN_LAST | G_SIGNAL_MUST_COLLECT,
+ 0, NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 1,
+ G_TYPE_VARIANT);
+
+ /**
+ * EUIAction::change-state:
+ * @self: an #EUIAction
+ * @new_state: a new state
+ *
+ * A signal emitted to set a new state to a stateful action.
+ *
+ * Since: 3.56
+ **/
+ signals[SIGNAL_CHANGE_STATE] = g_signal_new ("change-state",
+ E_TYPE_UI_ACTION,
+ G_SIGNAL_RUN_LAST | G_SIGNAL_MUST_COLLECT,
+ 0, NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 1,
+ G_TYPE_VARIANT);
+
+ /**
+ * EUIAction::changed:
+ * @self: an #EUIAction
+ *
+ * A signal emitted when the action changes its content.
+ * It's meant to be called when the menu content corresponding
+ * to this action should be regenerated, similar to when
+ * the #EUIAction:is-visible property changes.
+ *
+ * Since: 3.56
+ **/
+ signals[SIGNAL_CHANGED] = g_signal_new ("changed",
+ E_TYPE_UI_ACTION,
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ 0, NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 0,
+ G_TYPE_NONE);
+
+ /**
+ * EUIAction::accel-added:
+ * @self: an #EUIAction
+ * @accel: the added accelerator string
+ *
+ * A signal emitted when the action has either set the primary
+ * accelerator or it has added a secondary accelerator.
+ *
+ * Since: 3.56
+ **/
+ signals[SIGNAL_ACCEL_ADDED] = g_signal_new ("accel-added",
+ E_TYPE_UI_ACTION,
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ 0, NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+
+ /**
+ * EUIAction::accel-removed:
+ * @self: an #EUIAction
+ * @accel: the removed accelerator string
+ *
+ * A signal emitted when the action has either unset the primary
+ * accelerator or it has removed a secondary accelerator.
+ * The @accel is the removed accelerator string.
+ *
+ * Since: 3.56
+ **/
+ signals[SIGNAL_ACCEL_REMOVED] = g_signal_new ("accel-removed",
+ E_TYPE_UI_ACTION,
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ 0, NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+}
+
+static void
+e_ui_action_init (EUIAction *self)
+{
+ self->sensitive = TRUE;
+ self->visible = TRUE;
+}
+
+/**
+ * e_ui_action_new:
+ * @map_name: a name of a #GActionMap the action belongs to
+ * @action_name: a name of the action to be created
+ * @parameter_type: (nullable): a type of the action parameter, as #GVariantType, or %NULL
+ *
+ * Creates a new #EUIAction.
+ *
+ * Returns: (transfer full): a new #EUIAction
+ *
+ * Since: 3.56
+ **/
+EUIAction *
+e_ui_action_new (const gchar *map_name,
+ const gchar *action_name,
+ const GVariantType *parameter_type)
+{
+ g_return_val_if_fail (map_name != NULL, NULL);
+ g_return_val_if_fail (action_name != NULL, NULL);
+
+ return g_object_new (E_TYPE_UI_ACTION,
+ "map-name", map_name,
+ "name", action_name,
+ "parameter-type", parameter_type,
+ NULL);
+}
+
+/**
+ * e_ui_action_new_stateful:
+ * @map_name: a name of a #GActionMap the action belongs to
+ * @action_name: a name of the action to be created
+ * @parameter_type: (nullable): a type of the action parameter, as #GVariantType, or %NULL
+ * @state: a #GVariant for the action to have set a state to
+ *
+ * Creates a new stateful #EUIAction. Actions with boolean state are
+ * usually presented as toggle buttons and menu items, while actions
+ * with string states are usually presented as radio buttons and menu
+ * items. Radio actions need to have set radio group with
+ * e_ui_action_set_radio_group().
+ *
+ * Returns: (transfer full): a new stateful #EUIAction
+ *
+ * Since: 3.56
+ **/
+EUIAction *
+e_ui_action_new_stateful (const gchar *map_name,
+ const gchar *action_name,
+ const GVariantType *parameter_type,
+ GVariant *state)
+{
+ g_return_val_if_fail (map_name != NULL, NULL);
+ g_return_val_if_fail (action_name != NULL, NULL);
+
+ return g_object_new (E_TYPE_UI_ACTION,
+ "map-name", map_name,
+ "name", action_name,
+ "parameter-type", parameter_type,
+ "state", state,
+ NULL);
+}
+
+/**
+ * e_ui_action_new_from_entry:
+ * @map_name: a name of a #GActionMap the action belongs to
+ * @entry: a single #EUIActionEntry to construct the action from
+ * @translation_domain: (nullable): a translation domain to use
+ * to localize the entry members for label and tooltip
+ *
+ * Creates a new #EUIAction from the @entry.
+ *
+ * Returns: (transfer full): a new #EUIAction
+ *
+ * Since: 3.56
+ **/
+EUIAction *
+e_ui_action_new_from_entry (const gchar *map_name,
+ const EUIActionEntry *entry,
+ const gchar *translation_domain)
+{
+ const GVariantType *parameter_type;
+ EUIAction *action;
+
+ g_return_val_if_fail (map_name != NULL, NULL);
+ g_return_val_if_fail (entry != NULL, NULL);
+
+ if (entry->parameter_type) {
+ if (!g_variant_type_string_is_valid (entry->parameter_type)) {
+ g_critical ("%s: the type string '%s' given as the parameter type for "
+ "action '%s' is not a valid GVariant type string. This action will not be added.",
+ G_STRFUNC, entry->parameter_type, entry->name);
+ return NULL;
+ }
+
+ parameter_type = G_VARIANT_TYPE (entry->parameter_type);
+ } else {
+ parameter_type = NULL;
+ }
+
+ if (entry->state) {
+ GError *error = NULL;
+ GVariant *state;
+
+ state = g_variant_parse (NULL, entry->state, NULL, NULL, &error);
+ if (state == NULL) {
+ g_critical ("%s: GVariant could not parse the state value given for action '%s' "
+ "('%s'): %s. This action will not be added.",
+ G_STRFUNC, entry->name, entry->state, error->message);
+ g_clear_error (&error);
+ return NULL;
+ }
+
+ action = e_ui_action_new_stateful (map_name, entry->name, parameter_type, state);
+
+ g_clear_pointer (&state, g_variant_unref);
+ } else {
+ action = e_ui_action_new (map_name, entry->name, parameter_type);
+ }
+
+ if (action) {
+ const gchar *domain = translation_domain;
+
+ if (!domain || !*domain)
+ domain = GETTEXT_PACKAGE;
+
+ e_ui_action_set_icon_name (action, entry->icon_name);
+ e_ui_action_set_label (action, entry->label && *entry->label ? g_dgettext (domain, entry->label) : NULL);
+ e_ui_action_set_accel (action, entry->accel);
+ e_ui_action_set_tooltip (action, entry->tooltip && *entry->tooltip ? g_dgettext (domain, entry->tooltip) : NULL);
+ }
+
+ return action;
+}
+
+/**
+ * e_ui_action_new_from_enum_entry:
+ * @map_name: a name of a #GActionMap the action belongs to
+ * @entry: a single #EUIActionEnumEntry to construct the action from
+ * @translation_domain: (nullable): a translation domain to use
+ * to localize the entry members for label and tooltip
+ *
+ * Creates a new #EUIAction from the @entry.
+ *
+ * Returns: (transfer full): a new #EUIAction
+ *
+ * Since: 3.56
+ **/
+EUIAction *
+e_ui_action_new_from_enum_entry (const gchar *map_name,
+ const EUIActionEnumEntry *entry,
+ const gchar *translation_domain)
+{
+ EUIAction *action;
+
+ g_return_val_if_fail (map_name != NULL, NULL);
+ g_return_val_if_fail (entry != NULL, NULL);
+
+ action = e_ui_action_new_stateful (map_name, entry->name, G_VARIANT_TYPE_INT32, g_variant_new_int32 (entry->state));
+
+ if (action) {
+ const gchar *domain = translation_domain;
+
+ if (!domain || !*domain)
+ domain = GETTEXT_PACKAGE;
+
+ e_ui_action_set_icon_name (action, entry->icon_name);
+ e_ui_action_set_label (action, entry->label && *entry->label ? g_dgettext (domain, entry->label) : NULL);
+ e_ui_action_set_accel (action, entry->accel);
+ e_ui_action_set_tooltip (action, entry->tooltip && *entry->tooltip ? g_dgettext (domain, entry->tooltip) : NULL);
+ }
+
+ return action;
+}
+
+/**
+ * e_ui_action_get_map_name:
+ * @self: an #EUIAction
+ *
+ * Get name of the action map. It usually matches a name of
+ * an associated #EUIActionGroup.
+ *
+ * Returns: action map name of the @self
+ *
+ * Since: 3.56
+ **/
+const gchar *
+e_ui_action_get_map_name (EUIAction *self)
+{
+ g_return_val_if_fail (E_IS_UI_ACTION (self), NULL);
+
+ return self->map_name;
+}
+
+/**
+ * e_ui_action_ref_target:
+ * @self: an #EUIAction
+ *
+ * References target of a stateful action. This is usable for radio
+ * actions, where the state contains the current state of the whole
+ * radio group, while the target is the value to be set in the group
+ * when the action is activated.
+ *
+ * Returns: (transfer full) (nullable): the target value of the @self,
+ * or %NULL, when the @self is not a radio action
+ *
+ * Since: 3.56
+ **/
+GVariant *
+e_ui_action_ref_target (EUIAction *self)
+{
+ g_return_val_if_fail (E_IS_UI_ACTION (self), NULL);
+
+ return self->target ? g_variant_ref (self->target) : NULL;
+}
+
+static void
+e_ui_action_set_state_without_radio_group (EUIAction *self,
+ GVariant *value)
+{
+ g_return_if_fail (E_IS_UI_ACTION (self));
+ g_return_if_fail (value != NULL);
+
+ if (!self->state || !g_variant_equal (self->state, value)) {
+ if (self->state)
+ g_variant_unref (self->state);
+
+ self->state = g_variant_ref_sink (value);
+
+ if (!self->target && !g_variant_is_of_type (self->state, G_VARIANT_TYPE_BOOLEAN))
+ self->target = g_variant_ref_sink (value);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_STATE]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIVE]);
+ }
+}
+
+/**
+ * e_ui_action_set_state:
+ * @self: an #EUIAction
+ * @value: (transfer full): a state to set, as a #GVariant
+ *
+ * Sets a state of the @self to @value.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_action_set_state (EUIAction *self,
+ GVariant *value)
+{
+ guint ii;
+
+ g_return_if_fail (E_IS_UI_ACTION (self));
+ g_return_if_fail (value != NULL);
+
+ g_variant_ref_sink (value);
+
+ if (self->radio_group) {
+ /* to have the whole group in the consistent state, because
+ any listener to notify::state may not get correct result
+ when checking whether the action is active or not */
+ for (ii = 0; ii < self->radio_group->len; ii++) {
+ EUIAction *item = g_ptr_array_index (self->radio_group, ii);
+ g_object_freeze_notify (G_OBJECT (item));
+ }
+ }
+
+ e_ui_action_set_state_without_radio_group (self, value);
+
+ if (self->radio_group) {
+ for (ii = 0; ii < self->radio_group->len; ii++) {
+ EUIAction *item = g_ptr_array_index (self->radio_group, ii);
+
+ if (item != self)
+ e_ui_action_set_state_without_radio_group (item, value);
+ }
+
+ for (ii = 0; ii < self->radio_group->len; ii++) {
+ EUIAction *item = g_ptr_array_index (self->radio_group, ii);
+ g_object_thaw_notify (G_OBJECT (item));
+ }
+ }
+
+ g_variant_unref (value);
+}
+
+/**
+ * e_ui_action_set_state_hint:
+ * @self: an #EUIAction
+ * @state_hint: (nullable) (transfer full): a state hint as a #GVariant, or %NULL
+ *
+ * Sets or unsets the action state hint.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_action_set_state_hint (EUIAction *self,
+ GVariant *state_hint)
+{
+ g_return_if_fail (E_IS_UI_ACTION (self));
+
+ if (self->state_hint == state_hint)
+ return;
+
+ if (state_hint)
+ g_variant_ref_sink (state_hint);
+
+ g_clear_pointer (&self->state_hint, g_variant_unref);
+ self->state_hint = state_hint;
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_STATE_HINT]);
+}
+
+/**
+ * e_ui_action_set_visible:
+ * @self: an #EUIAction
+ * @visible: a value to set
+ *
+ * Sets whether the @self is visible.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_action_set_visible (EUIAction *self,
+ gboolean visible)
+{
+ GObject *object;
+
+ g_return_if_fail (E_IS_UI_ACTION (self));
+
+ if ((!self->visible) == (!visible))
+ return;
+
+ self->visible = visible;
+
+ object = G_OBJECT (self);
+ g_object_freeze_notify (object);
+ g_object_notify_by_pspec (object, properties[PROP_VISIBLE]);
+ g_object_notify_by_pspec (object, properties[PROP_IS_VISIBLE]);
+ g_object_thaw_notify (object);
+}
+
+/**
+ * e_ui_action_get_visible:
+ * @self: an #EUIAction
+ *
+ * Gets whether the action is visible. It does not consider the associated
+ * group visibility; use e_ui_action_is_visible() instead.
+ *
+ * Returns: whether the action itself is visible
+ *
+ * Since: 3.56
+ **/
+gboolean
+e_ui_action_get_visible (EUIAction *self)
+{
+ g_return_val_if_fail (E_IS_UI_ACTION (self), FALSE);
+
+ return self->visible;
+}
+
+/**
+ * e_ui_action_set_sensitive:
+ * @self: an #EUIAction
+ * @sensitive: a value to set
+ *
+ * Sets whether the action is sensitive.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_action_set_sensitive (EUIAction *self,
+ gboolean sensitive)
+{
+ GObject *object;
+
+ g_return_if_fail (E_IS_UI_ACTION (self));
+
+ if ((!self->sensitive) == (!sensitive))
+ return;
+
+ self->sensitive = sensitive;
+
+ object = G_OBJECT (self);
+ g_object_freeze_notify (object);
+ g_object_notify_by_pspec (object, properties[PROP_SENSITIVE]);
+ g_object_notify_by_pspec (object, properties[PROP_ENABLED]);
+ g_object_thaw_notify (object);
+}
+
+/**
+ * e_ui_action_get_sensitive:
+ * @self: an #EUIAction
+ *
+ * Gets whether the action is sensitive. It does not consider the associated
+ * group sensitivity; use g_action_get_enabled() instead.
+ *
+ * Returns: whether the action itself is sensitive
+ *
+ * Since: 3.56
+ **/
+gboolean
+e_ui_action_get_sensitive (EUIAction *self)
+{
+ g_return_val_if_fail (E_IS_UI_ACTION (self), FALSE);
+
+ return self->sensitive;
+}
+
+/**
+ * e_ui_action_is_visible:
+ * @self: an #EUIAction
+ *
+ * Gets whether the action itself and the associated #EUIActionGroup,
+ * if any, are both visible. Use e_ui_action_get_visible() to get
+ * visibility of the action itself.
+ *
+ * Returns: whether the @self with the associated action group are visible
+ *
+ * Since: 3.56
+ **/
+gboolean
+e_ui_action_is_visible (EUIAction *self)
+{
+ g_return_val_if_fail (E_IS_UI_ACTION (self), FALSE);
+
+ return self->visible && (!self->action_group || e_ui_action_group_get_visible (self->action_group));
+}
+
+/**
+ * e_ui_action_set_icon_name:
+ * @self: an #EUIAction
+ * @icon_name: (nullable): an icon name, or %NULL
+ *
+ * Sets or unsets and icon name for the @self. It can be a standard icon name,
+ * or a text with prefix "gicon::", which are generated by the action owner
+ * through #EUIManager::create-gicon signal. The signal is executed without
+ * the "gicon::" prefix.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_action_set_icon_name (EUIAction *self,
+ const gchar *icon_name)
+{
+ g_return_if_fail (E_IS_UI_ACTION (self));
+
+ if (e_util_strcmp0 (self->icon_name, icon_name) == 0)
+ return;
+
+ g_free (self->icon_name);
+ self->icon_name = g_strdup (icon_name);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ICON_NAME]);
+}
+
+/**
+ * e_ui_action_get_icon_name:
+ * @self: an #EUIAction
+ *
+ * Gets the icon name of the @self.
+ *
+ * Returns: (nullable): an icon name for the @self, or %NULL, when none is set
+ *
+ * Since: 3.56
+ **/
+const gchar *
+e_ui_action_get_icon_name (EUIAction *self)
+{
+ g_return_val_if_fail (E_IS_UI_ACTION (self), NULL);
+
+ return self->icon_name;
+}
+
+/**
+ * e_ui_action_set_label:
+ * @self: an #EUIAction
+ * @label: (nullable): a label to set, or %NULL
+ *
+ * Sets a label for the action. The label should be already localized.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_action_set_label (EUIAction *self,
+ const gchar *label)
+{
+ g_return_if_fail (E_IS_UI_ACTION (self));
+
+ if (e_util_strcmp0 (self->label, label) == 0)
+ return;
+
+ g_free (self->label);
+ self->label = g_strdup (label);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LABEL]);
+}
+
+/**
+ * e_ui_action_get_label:
+ * @self: an #EUIAction
+ *
+ * Gets a label of the @self.
+ *
+ * Returns: (nullable): a label of the @self, or %NULL, when none is set
+ *
+ * Since: 3.56
+ **/
+const gchar *
+e_ui_action_get_label (EUIAction *self)
+{
+ g_return_val_if_fail (E_IS_UI_ACTION (self), NULL);
+
+ return self->label;
+}
+
+/**
+ * e_ui_action_set_accel:
+ * @self: an #EUIAction
+ * @accel: (nullable): an accelerator string, or %NULL
+ *
+ * Sets or unsets an accelerator string for the @self.
+ * The @accel should be parseable by the gtk_accelerator_parse()
+ * function.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_action_set_accel (EUIAction *self,
+ const gchar *accel)
+{
+ g_return_if_fail (E_IS_UI_ACTION (self));
+
+ if (e_util_strcmp0 (self->accel, accel) == 0)
+ return;
+
+ if (self->accel)
+ g_signal_emit (self, signals[SIGNAL_ACCEL_REMOVED], 0, self->accel, NULL);
+
+ g_free (self->accel);
+ self->accel = g_strdup (accel);
+
+ if (self->accel)
+ g_signal_emit (self, signals[SIGNAL_ACCEL_ADDED], 0, self->accel, NULL);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACCEL]);
+}
+
+/**
+ * e_ui_action_get_accel:
+ * @self: an #EUIAction
+ *
+ * Gets an accelarator string for the @self.
+ *
+ * Returns: (nullable): an accelerator string for the @self, or %NULL, when none is set
+ *
+ * Since: 3.56
+ **/
+const gchar *
+e_ui_action_get_accel (EUIAction *self)
+{
+ g_return_val_if_fail (E_IS_UI_ACTION (self), NULL);
+
+ return self->accel;
+}
+
+/**
+ * e_ui_action_add_secondary_accel:
+ * @self: an #EUIAction
+ * @accel: (not nullable): an accelerator string
+ *
+ * Adds a secondary accelerator string for the @self. This is in addition
+ * to the "accel" property, which is meant to be the main accelerator,
+ * shown in the GUI and such.
+ *
+ * The @accel should be parseable by the gtk_accelerator_parse()
+ * function.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_action_add_secondary_accel (EUIAction *self,
+ const gchar *accel)
+{
+ g_return_if_fail (E_IS_UI_ACTION (self));
+ g_return_if_fail (accel != NULL);
+
+ if (!self->secondary_accels) {
+ self->secondary_accels = g_ptr_array_new_with_free_func (g_free);
+ } else {
+ guint ii;
+
+ for (ii = 0; ii < self->secondary_accels->len; ii++) {
+ const gchar *existing = g_ptr_array_index (self->secondary_accels, ii);
+ if (e_util_strcmp0 (existing, accel) == 0)
+ return;
+ }
+ }
+
+ g_ptr_array_add (self->secondary_accels, g_strdup (accel));
+
+ g_signal_emit (self, signals[SIGNAL_ACCEL_ADDED], 0, accel, NULL);
+}
+
+/**
+ * e_ui_action_get_secondary_accels:
+ * @self: an #EUIAction
+ *
+ * Gets an array of the secondary accelarators for the @self. The caller should
+ * not modify the array in any way.
+ *
+ * Returns: (nullable) (transfer container) (element-type utf-8): an array
+ * of the secondary accelerator strings for the @self, or %NULL, when none is set
+ *
+ * Since: 3.56
+ **/
+GPtrArray *
+e_ui_action_get_secondary_accels (EUIAction *self)
+{
+ g_return_val_if_fail (E_IS_UI_ACTION (self), NULL);
+
+ return self->secondary_accels;
+}
+
+/**
+ * e_ui_action_remove_secondary_accels:
+ * @self: an #EUIAction
+ *
+ * Removes all secondary accelerator strings.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_action_remove_secondary_accels (EUIAction *self)
+{
+ g_return_if_fail (E_IS_UI_ACTION (self));
+
+ if (self->secondary_accels) {
+ guint ii;
+
+ for (ii = 0; ii < self->secondary_accels->len; ii++) {
+ const gchar *accel = g_ptr_array_index (self->secondary_accels, ii);
+
+ g_signal_emit (self, signals[SIGNAL_ACCEL_REMOVED], 0, accel, NULL);
+ }
+ }
+
+ g_clear_pointer (&self->secondary_accels, g_ptr_array_unref);
+}
+
+/**
+ * e_ui_action_set_tooltip:
+ * @self: an #EUIAction
+ * @tooltip: (nullable): a tooltip text to set, or %NULL
+ *
+ * Sets or unsets a tooltip text (not markup) for the @self.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_action_set_tooltip (EUIAction *self,
+ const gchar *tooltip)
+{
+ g_return_if_fail (E_IS_UI_ACTION (self));
+
+ if (e_util_strcmp0 (self->tooltip, tooltip) == 0)
+ return;
+
+ g_free (self->tooltip);
+ self->tooltip = g_strdup (tooltip);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TOOLTIP]);
+}
+
+/**
+ * e_ui_action_get_tooltip:
+ * @self: an #EUIAction
+ *
+ * Gets tooltip text for the @self.
+ *
+ * Returns: (nullable): a tooltip text (not markup) for the @self, or %NULL, when none is set.
+ *
+ * Since: 3.56
+ **/
+const gchar *
+e_ui_action_get_tooltip (EUIAction *self)
+{
+ g_return_val_if_fail (E_IS_UI_ACTION (self), NULL);
+
+ return self->tooltip;
+}
+
+/**
+ * e_ui_action_set_radio_group:
+ * @self: an #EUIAction
+ * @radio_group: (nullable) (transfer none) (element-type EUIAction): a radio group to use, or %NULL
+ *
+ * Stateful actions can be grouped together, by setting the same radio
+ * group to each action. When not %NULL, the @self references the @radio_group,
+ * thus the caller can just create one array, set it to each action
+ * and then unref it.
+ *
+ * An action can be in a single radio group only. Trying to set a different
+ * radio group when one is already set is considered a programming error.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_action_set_radio_group (EUIAction *self,
+ GPtrArray *radio_group)
+{
+ g_return_if_fail (E_IS_UI_ACTION (self));
+
+ if (!radio_group) {
+ if (self->radio_group) {
+ g_ptr_array_remove (self->radio_group, self);
+ g_clear_pointer (&self->radio_group, g_ptr_array_unref);
+ }
+ return;
+ }
+
+ if (self->radio_group && self->radio_group != radio_group) {
+ g_warning ("%s: Action '%s' is already in another radio group", G_STRFUNC, self->name);
+ return;
+ }
+
+ if (self->radio_group == radio_group)
+ return;
+
+ g_return_if_fail (self->radio_group == NULL);
+
+ self->radio_group = g_ptr_array_ref (radio_group);
+ g_ptr_array_add (self->radio_group, self); /* do not ref the 'self', due to circular dependency */
+}
+
+/**
+ * e_ui_action_get_radio_group:
+ * @self: an #EUIAction
+ *
+ * Gets the radio group the @self is part of. The array is owned by the @self,
+ * it cannot be modified neither freed by the caller.
+ *
+ * Returns: (nullable) (transfer none) (element-type EUIAction): a radio group
+ * the @self is part of, or %NULL
+ *
+ * Since: 3.56
+ **/
+GPtrArray *
+e_ui_action_get_radio_group (EUIAction *self)
+{
+ g_return_val_if_fail (E_IS_UI_ACTION (self), NULL);
+
+ return self->radio_group;
+}
+
+/**
+ * e_ui_action_set_action_group:
+ * @self: an #EUIAction
+ * @action_group: (nullable): an #EUIActionGroup to set, or %NULL
+ *
+ * Sets the @self as being part of the @action_group, or unsets the group,
+ * when it's %NULL. An action cannot be part of multiple action groups,
+ * thus when trying to set a different action group the action is removed
+ * from the previous group (unlike e_ui_action_set_radio_group(), which
+ * does not move an action between radio groups automatically).
+ *
+ * The @action_group usually matches the @EUIAction:map-name property.
+ *
+ * See e_ui_action_get_sensitive(), e_ui_action_get_visible(),
+ * e_ui_action_is_visible().
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_action_set_action_group (EUIAction *self,
+ struct _EUIActionGroup *action_group)
+{
+ GObject *object;
+ gboolean old_enabled, old_is_visible;
+
+ g_return_if_fail (E_IS_UI_ACTION (self));
+
+ if (self->action_group == action_group)
+ return;
+
+ old_enabled = e_ui_action_get_enabled (G_ACTION (self));
+ old_is_visible = e_ui_action_is_visible (self);
+
+ if (self->action_group) {
+ EUIActionGroup *old_action_group = self->action_group;
+ /* to avoid recursion */
+ self->action_group = NULL;
+ e_ui_action_group_remove (old_action_group, self);
+ }
+
+ if (action_group) {
+ self->action_group = action_group;
+ e_ui_action_group_add (action_group, self);
+ }
+
+ object = G_OBJECT (self);
+ g_object_freeze_notify (object);
+
+ if ((!old_enabled) != (!e_ui_action_get_enabled (G_ACTION (self))))
+ g_object_notify_by_pspec (object, properties[PROP_ENABLED]);
+
+ if ((!old_is_visible) != (!e_ui_action_is_visible (self)))
+ g_object_notify_by_pspec (object, properties[PROP_IS_VISIBLE]);
+
+ g_object_thaw_notify (object);
+}
+
+/**
+ * e_ui_action_get_action_group:
+ * @self: an #EUIAction
+ *
+ * Gets an action group the @self is part of.
+ *
+ * Returns: (transfer none) (nullable): an #EUIActionGroup the @self is part of,
+ * or %NULL, when none is set
+ *
+ * Since: 3.56
+ **/
+struct _EUIActionGroup *
+e_ui_action_get_action_group (EUIAction *self)
+{
+ g_return_val_if_fail (E_IS_UI_ACTION (self), NULL);
+
+ return self->action_group;
+}
+
+/**
+ * e_ui_action_get_active:
+ * @self: an #EUIAction
+ *
+ * Checks whether the @self state is the active, aka current, value.
+ *
+ * For toggle actions (with boolean state) returns whether the state is %TRUE.
+ * For radio actions returns whether the state matches the action target.
+ * For stateless actions returns FALSE.
+ *
+ * Returns: whether the @self is active
+ *
+ * Since: 3.56
+ **/
+gboolean
+e_ui_action_get_active (EUIAction *self)
+{
+ g_return_val_if_fail (E_IS_UI_ACTION (self), FALSE);
+
+ if (!self->state)
+ return FALSE;
+
+ if (self->target)
+ return g_variant_equal (self->state, self->target);
+
+ return g_variant_is_of_type (self->state, G_VARIANT_TYPE_BOOLEAN) &&
+ g_variant_get_boolean (self->state);
+}
+
+/**
+ * e_ui_action_set_active:
+ * @self: an #EUIAction
+ * @active: value to set
+ *
+ * Sets whether the @self state is the @active, aka current, value.
+ *
+ * For toggle actions (with boolean state) sets the @active directly.
+ * For radio actions sets the state to the @self target when @active is %TRUE,
+ * otherwise does nothing. For stateless actions does nothing.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_action_set_active (EUIAction *self,
+ gboolean active)
+{
+ g_return_if_fail (E_IS_UI_ACTION (self));
+
+ if (!self->state)
+ return;
+
+ if (g_variant_is_of_type (self->state, G_VARIANT_TYPE_BOOLEAN)) {
+ e_ui_action_set_state (self, g_variant_new_boolean (active));
+ return;
+ }
+
+ if (self->target && active)
+ e_ui_action_set_state (self, self->target);
+}
+
+/**
+ * e_ui_action_emit_changed:
+ * @self: an #EUIAction
+ *
+ * Emits the #EUIAction::changed signal on the @self.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_action_emit_changed (EUIAction *self)
+{
+ g_return_if_fail (E_IS_UI_ACTION (self));
+
+ g_signal_emit (self, signals[SIGNAL_CHANGED], 0, NULL);
+}
+
+/**
+ * e_ui_action_util_gvalue_to_enum_state:
+ * @binding: a #GBinding
+ * @from_value: a source #GValue
+ * @to_value: a destination #GValue
+ * @user_data: unused binding user data
+ *
+ * A utility function, which can be used as a transformation function
+ * of a #GBinding from an enum property to an enum state of an #EUIAction
+ * created by e_ui_action_new_from_enum_entry().
+ *
+ * Returns: %TRUE
+ *
+ * Since: 3.56
+ **/
+gboolean
+e_ui_action_util_gvalue_to_enum_state (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ gint value;
+
+ if (G_VALUE_HOLDS_ENUM (from_value))
+ value = g_value_get_enum (from_value);
+ else
+ value = g_value_get_int (from_value);
+
+ g_value_set_variant (to_value, g_variant_new_int32 (value));
+
+ return TRUE;
+}
+
+/**
+ * e_ui_action_util_enum_state_to_gvalue:
+ * @binding: a #GBinding
+ * @from_value: a source #GValue
+ * @to_value: a destination #GValue
+ * @user_data: unused binding user data
+ *
+ * A utility function, which can be used as a transformation function
+ * of a #GBinding from an enum state of an #EUIAction created by
+ * e_ui_action_new_from_enum_entry() to an enum property.
+ *
+ * Returns: %TRUE
+ *
+ * Since: 3.56
+ **/
+gboolean
+e_ui_action_util_enum_state_to_gvalue (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ GVariant *value = g_value_get_variant (from_value);
+
+ if (G_VALUE_HOLDS_ENUM (to_value))
+ g_value_set_enum (to_value, value ? g_variant_get_int32 (value) : -1);
+ else
+ g_value_set_int (to_value, value ? g_variant_get_int32 (value) : -1);
+
+ return TRUE;
+}
+
+/**
+ * e_ui_action_util_assign_to_widget:
+ * @action: an #EUIAction
+ * @widget: a #GtkWidget
+ *
+ * Assigns @action to the @widget, syncing visible, sensitive and tooltip properties,
+ * together with the actionable properties.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_action_util_assign_to_widget (EUIAction *action,
+ GtkWidget *widget)
+{
+ gchar full_action_name[128];
+ gint would_copy_bytes;
+ GtkActionable *actionable;
+ GVariant *target;
+
+ g_return_if_fail (E_IS_UI_ACTION (action));
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ would_copy_bytes = g_snprintf (full_action_name, sizeof (full_action_name), "%s.%s",
+ e_ui_action_get_map_name (action),
+ g_action_get_name (G_ACTION (action)));
+ g_warn_if_fail (would_copy_bytes < sizeof (full_action_name) - 1);
+
+ actionable = GTK_ACTIONABLE (widget);
+ target = e_ui_action_ref_target (action);
+
+ gtk_actionable_set_action_target_value (actionable, target);
+ gtk_actionable_set_action_name (actionable, full_action_name);
+
+ g_clear_pointer (&target, g_variant_unref);
+
+ e_binding_bind_property (action, "visible",
+ widget, "visible",
+ G_BINDING_SYNC_CREATE);
+
+ e_binding_bind_property (action, "sensitive",
+ widget, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ e_binding_bind_property (action, "tooltip",
+ widget, "tooltip-text",
+ G_BINDING_SYNC_CREATE);
+}
diff --git a/src/e-util/e-ui-action.h b/src/e-util/e-ui-action.h
new file mode 100644
index 0000000000..f4d99e0873
--- /dev/null
+++ b/src/e-util/e-ui-action.h
@@ -0,0 +1,125 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * SPDX-FileCopyrightText: (C) 2024 Red Hat (www.redhat.com>
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only should be included directly."
+#endif
+
+#ifndef E_UI_ACTION_H
+#define E_UI_ACTION_H
+
+#include
+
+G_BEGIN_DECLS
+
+struct _EUIActionGroup;
+struct _GtkWidget;
+
+#define E_TYPE_UI_ACTION e_ui_action_get_type ()
+
+G_DECLARE_FINAL_TYPE (EUIAction, e_ui_action, E, UI_ACTION, GObject)
+
+typedef void (* EUIActionFunc) (EUIAction *action,
+ GVariant *value,
+ gpointer user_data);
+
+typedef struct _EUIActionEntry {
+ const gchar *name;
+ const gchar *icon_name;
+ const gchar *label;
+ const gchar *accel;
+ const gchar *tooltip;
+ EUIActionFunc activate;
+ const gchar *parameter_type;
+ const gchar *state;
+ EUIActionFunc change_state;
+} EUIActionEntry;
+
+typedef struct _EUIActionEnumEntry {
+ const gchar *name;
+ const gchar *icon_name;
+ const gchar *label;
+ const gchar *accel;
+ const gchar *tooltip;
+ EUIActionFunc activate;
+ gint state;
+} EUIActionEnumEntry;
+
+/* The EUIAction implements GAction, thus use its API to get to the name and other properties. */
+
+EUIAction * e_ui_action_new (const gchar *map_name,
+ const gchar *action_name,
+ const GVariantType *parameter_type);
+EUIAction * e_ui_action_new_stateful (const gchar *map_name,
+ const gchar *action_name,
+ const GVariantType *parameter_type,
+ GVariant *state);
+EUIAction * e_ui_action_new_from_entry (const gchar *map_name,
+ const EUIActionEntry *entry,
+ const gchar *translation_domain);
+EUIAction * e_ui_action_new_from_enum_entry (const gchar *map_name,
+ const EUIActionEnumEntry *entry,
+ const gchar *translation_domain);
+const gchar * e_ui_action_get_map_name (EUIAction *self);
+GVariant * e_ui_action_ref_target (EUIAction *self);
+void e_ui_action_set_state (EUIAction *self,
+ GVariant *value);
+void e_ui_action_set_state_hint (EUIAction *self,
+ GVariant *state_hint);
+void e_ui_action_set_visible (EUIAction *self,
+ gboolean visible);
+gboolean e_ui_action_get_visible (EUIAction *self);
+void e_ui_action_set_sensitive (EUIAction *self,
+ gboolean sensitive);
+gboolean e_ui_action_get_sensitive (EUIAction *self);
+gboolean e_ui_action_is_visible (EUIAction *self);
+void e_ui_action_set_icon_name (EUIAction *self,
+ const gchar *icon_name);
+const gchar * e_ui_action_get_icon_name (EUIAction *self);
+void e_ui_action_set_label (EUIAction *self,
+ const gchar *label);
+const gchar * e_ui_action_get_label (EUIAction *self);
+void e_ui_action_set_accel (EUIAction *self,
+ const gchar *accel);
+const gchar * e_ui_action_get_accel (EUIAction *self);
+void e_ui_action_add_secondary_accel (EUIAction *self,
+ const gchar *accel);
+GPtrArray * e_ui_action_get_secondary_accels(EUIAction *self); /* const gchar * */
+void e_ui_action_remove_secondary_accels
+ (EUIAction *self);
+void e_ui_action_set_tooltip (EUIAction *self,
+ const gchar *tooltip);
+const gchar * e_ui_action_get_tooltip (EUIAction *self);
+void e_ui_action_set_radio_group (EUIAction *self,
+ GPtrArray *radio_group);
+/* const */ GPtrArray *
+ e_ui_action_get_radio_group (EUIAction *self);
+void e_ui_action_set_action_group (EUIAction *self,
+ struct _EUIActionGroup *action_group);
+struct _EUIActionGroup *
+ e_ui_action_get_action_group (EUIAction *self);
+gboolean e_ui_action_get_active (EUIAction *self);
+void e_ui_action_set_active (EUIAction *self,
+ gboolean active);
+void e_ui_action_emit_changed (EUIAction *self);
+
+gboolean e_ui_action_util_gvalue_to_enum_state
+ (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data);
+gboolean e_ui_action_util_enum_state_to_gvalue
+ (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data);
+void e_ui_action_util_assign_to_widget
+ (EUIAction *action,
+ struct _GtkWidget *widget);
+
+G_END_DECLS
+
+#endif /* E_UI_ACTION_H */
diff --git a/src/e-util/e-ui-manager.c b/src/e-util/e-ui-manager.c
new file mode 100644
index 0000000000..e2992afb26
--- /dev/null
+++ b/src/e-util/e-ui-manager.c
@@ -0,0 +1,2229 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * SPDX-FileCopyrightText: (C) 2024 Red Hat (www.redhat.com>
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "evolution-config.h"
+
+#include
+
+#include
+
+#include "e-headerbar.h"
+#include "e-headerbar-button.h"
+#include "e-misc-utils.h"
+#include "e-ui-action.h"
+#include "e-ui-menu.h"
+#include "e-ui-parser.h"
+#include "e-util-enumtypes.h"
+
+#include "e-ui-manager.h"
+
+/**
+ * SECTION: e-ui-manager
+ * @include: e-util/e-util.h
+ * @short_description: a UI manager
+ *
+ * The #EUIManager is a central point of managing headerbars, toolbars, menubars
+ * and context menus in the application.
+ * The #EUIParser is used to read the .eui definition from an XML file or data.
+ * Each item in the definition corresponds to an #EUIAction, which should be added
+ * with the e_ui_manager_add_actions() or e_ui_manager_add_actions_enum() before
+ * the corresponding items are created with the e_ui_manager_create_item().
+ *
+ * The e_ui_manager_set_action_groups_widget() is used to assign a widget to
+ * which the action groups should be inserted (for example a #GtkWindow, but
+ * can be other as well).
+ *
+ * The #GtkAccelGroup returned from the e_ui_manager_get_accel_group() needs
+ * to be added to the #GtkWindow with gtk_window_add_accel_group().
+ *
+ * The object is not thread-safe, it's meant to be used only from
+ * the main/GUI thread.
+ *
+ * Since: 3.56
+ **/
+
+struct _EUIManager {
+ GObject parent;
+
+ EUIParser *parser;
+ GtkAccelGroup *accel_group;
+ GHashTable *action_groups; /* gchar * ~> EUIActionGroup * */
+ GHashTable *gicons; /* gchar * ~> GIcon * */
+ GHashTable *groups; /* gchar * ~> GPtrArray * { EUIAction }; used for radio groups */
+
+ GWeakRef action_groups_widget;
+
+ guint frozen;
+ gboolean changed_while_frozen;
+};
+
+enum {
+ SIGNAL_CHANGED,
+ SIGNAL_FREEZE,
+ SIGNAL_THAW,
+ SIGNAL_CREATE_ITEM,
+ SIGNAL_CREATE_GICON,
+ SIGNAL_IGNORE_ACCEL,
+ N_SIGNALS
+};
+
+static guint signals[N_SIGNALS] = { 0, };
+
+G_DEFINE_TYPE (EUIManager, e_ui_manager, G_TYPE_OBJECT)
+
+static void
+e_ui_manager_gather_groups_recr (EUIManager *self,
+ EUIElement *elem)
+{
+ if (!elem)
+ return;
+
+ if (e_ui_element_get_kind (elem) == E_UI_ELEMENT_KIND_ITEM) {
+ const gchar *group = e_ui_element_item_get_group (elem);
+
+ if (group && *group && e_ui_element_item_get_action (elem)) {
+ EUIAction *action;
+
+ action = e_ui_manager_get_action (self, e_ui_element_item_get_action (elem));
+ if (action) {
+ GPtrArray *group_array;
+
+ group_array = g_hash_table_lookup (self->groups, group);
+
+ if (!group_array) {
+ group_array = g_ptr_array_new ();
+ g_hash_table_insert (self->groups, g_strdup (group), group_array);
+ }
+
+ e_ui_action_set_radio_group (action, group_array);
+ } else {
+ g_warning ("%s: Action '%s' for group '%s' not found", G_STRFUNC, e_ui_element_item_get_action (elem), group);
+ }
+ }
+ } else if (e_ui_element_get_n_children (elem) > 0) {
+ guint ii, sz = e_ui_element_get_n_children (elem);
+ for (ii = 0; ii < sz; ii++) {
+ EUIElement *child = e_ui_element_get_child (elem, ii);
+ e_ui_manager_gather_groups_recr (self, child);
+ }
+ }
+}
+
+static void
+e_ui_manager_gather_groups (EUIManager *self)
+{
+ GHashTableIter iter;
+ gpointer value;
+
+ g_hash_table_iter_init (&iter, self->groups);
+
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
+ GPtrArray *group_array = value;
+ guint ii;
+
+ /* go from the back, the unset modifies content of the group_array */
+ for (ii = group_array->len; ii-- > 0;) {
+ EUIAction *action = g_ptr_array_index (group_array, ii);
+ e_ui_action_set_radio_group (action, NULL);
+ }
+ }
+
+ g_hash_table_remove_all (self->groups);
+
+ e_ui_manager_gather_groups_recr (self, e_ui_parser_get_root (self->parser));
+
+ /* update the state, thus there's only one (or none) group item selected */
+ g_hash_table_iter_init (&iter, self->groups);
+
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
+ GPtrArray *group_array = value;
+
+ if (group_array->len) {
+ EUIAction *action = g_ptr_array_index (group_array, 0);
+ GVariant *state;
+
+ state = g_action_get_state (G_ACTION (action));
+ if (state) {
+ /* while setting the same value, the function will update other group members with it */
+ e_ui_action_set_state (action, state);
+ g_variant_unref (state);
+ }
+ }
+ }
+}
+
+static void
+e_ui_manager_changed (EUIManager *self)
+{
+ if (self->frozen) {
+ self->changed_while_frozen = TRUE;
+ return;
+ }
+
+ e_ui_manager_gather_groups (self);
+ g_signal_emit (self, signals[SIGNAL_CHANGED], 0, NULL);
+}
+
+static void
+e_ui_manager_parser_changed_cb (EUIParser *parser,
+ gpointer user_data)
+{
+ EUIManager *self = user_data;
+
+ e_ui_manager_changed (self);
+}
+
+static void
+e_ui_manager_dispose (GObject *object)
+{
+ EUIManager *self = E_UI_MANAGER (object);
+
+ e_ui_manager_set_action_groups_widget (self, NULL);
+
+ G_OBJECT_CLASS (e_ui_manager_parent_class)->dispose (object);
+}
+
+static void
+e_ui_manager_finalize (GObject *object)
+{
+ EUIManager *self = E_UI_MANAGER (object);
+
+ g_clear_object (&self->parser);
+ g_clear_object (&self->accel_group);
+ g_clear_pointer (&self->action_groups, g_hash_table_unref);
+ g_clear_pointer (&self->gicons, g_hash_table_unref);
+ g_clear_pointer (&self->groups, g_hash_table_unref);
+ g_weak_ref_clear (&self->action_groups_widget);
+
+ G_OBJECT_CLASS (e_ui_manager_parent_class)->finalize (object);
+}
+
+static void
+e_ui_manager_class_init (EUIManagerClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = e_ui_manager_dispose;
+ object_class->finalize = e_ui_manager_finalize;
+
+ /* void changed (EUIManager *manager); */
+ /**
+ * EUIManager::changed:
+ * @manager: an #EUIManager
+ *
+ * A signal called when the @manager content changed. It's a signal to
+ * regenerate the UI elements.
+ *
+ * Since: 3.56
+ **/
+ signals[SIGNAL_CHANGED] = g_signal_new ("changed",
+ E_TYPE_UI_MANAGER,
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ 0,
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 0,
+ G_TYPE_NONE);
+
+ /* void freeze (EUIManager *manager); */
+ /**
+ * EUIManager::freeze:
+ * @manager: an #EUIManager
+ *
+ * A signal called when the @manager has called e_ui_manager_freeze().
+ *
+ * Since: 3.56
+ **/
+ signals[SIGNAL_FREEZE] = g_signal_new ("freeze",
+ E_TYPE_UI_MANAGER,
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ 0,
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 0,
+ G_TYPE_NONE);
+
+ /* void thaw (EUIManager *manager,
+ gboolean changed_while_frozen); */
+ /**
+ * EUIManager::thaw:
+ * @manager: an #EUIManager
+ * @changed_while_frozen: whether changed while had been frozen
+ *
+ * A signal called when the @manager has called e_ui_manager_thaw().
+ * When the @changed_while_frozen is set to %TRUE, there will also
+ * be called the #EUIManager::changed signal once the @manager
+ * is completely unfrozen.
+ *
+ * Since: 3.56
+ **/
+ signals[SIGNAL_THAW] = g_signal_new ("thaw",
+ E_TYPE_UI_MANAGER,
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ 0,
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 1,
+ G_TYPE_BOOLEAN);
+
+ /* gboolean create_item (EUIManager *manager,
+ EUIElement *elem,
+ EUIAction *action,
+ EUIElementKind for_kind,
+ GObject **out_item); */
+ /**
+ * EUIManager::create-item: (skip)
+ * @manager: an #EUIManager
+ * @elem: an #EUIElement to create the item for
+ * @action: an associated @EUIAction for the new item
+ * @for_kind: for which part of the UI the item should be created; it influences the type of the returned item
+ * @out_item: (out) (transfer full): a location to set the created item to
+ *
+ * Return %TRUE, when could create the item described by @elem with associated @action and
+ * sets it into the @out_item. The @for_kind can only be %E_UI_ELEMENT_KIND_HEADERBAR,
+ * %E_UI_ELEMENT_KIND_TOOLBAR or %E_UI_ELEMENT_KIND_MENU.
+ *
+ * For the %E_UI_ELEMENT_KIND_HEADERBAR any widget can be returned, though the mostly
+ * used might be an #EHeaderBarButton.
+ *
+ * For the %E_UI_ELEMENT_KIND_TOOLBAR only a #GtkToolItem descendant is expected.
+ *
+ * For the %E_UI_ELEMENT_KIND_MENU only a #GMenuItem descendant is expected.
+ *
+ * It's expected only one handler can create the item, thus the first
+ * which returns %TRUE will stop further signal emission.
+ *
+ * Returns: %TRUE when the item was created and set into the @out_item,
+ * %FALSE when cannot create the item
+ *
+ * Since: 3.56
+ **/
+ signals[SIGNAL_CREATE_ITEM] = g_signal_new ("create-item",
+ E_TYPE_UI_MANAGER,
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ 0,
+ g_signal_accumulator_true_handled, NULL,
+ NULL,
+ G_TYPE_BOOLEAN, 4,
+ G_TYPE_POINTER, /* EUIElement */
+ E_TYPE_UI_ACTION,
+ E_TYPE_UI_ELEMENT_KIND,
+ G_TYPE_POINTER);
+
+ /* gboolean create_gicon (EUIManager *manager,
+ const gchar *name,
+ GIcon **out_gicon); */
+ /**
+ * EUIManager::create-gicon:
+ * @manager: an #EUIManager
+ * @name: a custom icon name
+ * @out_gicon: (out) (transfer full): an output location to store a created #GIcon to
+ *
+ * The actions can have defined custom icons as "gicon::name",
+ * aka with a "gicon::" prefix, which are looked for by the name (without
+ * the special prefix) using this callback.
+ *
+ * It's expected only one handler can create the #GIcon, thus the first
+ * which returns %TRUE will stop further signal emission.
+ *
+ * Returns: %TRUE when the item was created and set into the @out_gicon,
+ * %FALSE when cannot create the item
+ *
+ * Since: 3.56
+ **/
+ signals[SIGNAL_CREATE_GICON] = g_signal_new ("create-gicon",
+ E_TYPE_UI_MANAGER,
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ 0,
+ g_signal_accumulator_true_handled, NULL,
+ NULL,
+ G_TYPE_BOOLEAN, 2,
+ G_TYPE_STRING,
+ G_TYPE_POINTER);
+
+ /* gboolean ignore_accel (EUIManager *manager,
+ EUIAction *action); */
+ /**
+ * EUIManager::ignore-accel:
+ * @manager: an #EUIManager
+ * @action: an #EUIAction whose accelerator is about to be activated
+ *
+ * The signal allows to ignore accelerators to be activated.
+ *
+ * Multiple signal handlers can be connected, the first which
+ * returns %TRUE will stop the signal emission and the accelerator
+ * will be ignored. When all handlers return %FALSE, the accelerator
+ * will be used and the @action will be activated.
+ *
+ * Returns: %TRUE, when the accel should be ignored, %FALSE to
+ * allow the accel to be activated.
+ *
+ * Since:3.56
+ **/
+ signals[SIGNAL_IGNORE_ACCEL] = g_signal_new ("ignore-accel",
+ E_TYPE_UI_MANAGER,
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ 0,
+ g_signal_accumulator_true_handled, NULL,
+ NULL,
+ G_TYPE_BOOLEAN, 1,
+ E_TYPE_UI_ACTION);
+}
+
+static void
+e_ui_manager_init (EUIManager *self)
+{
+ g_weak_ref_init (&self->action_groups_widget, NULL);
+
+ self->parser = e_ui_parser_new ();
+ self->accel_group = gtk_accel_group_new ();
+ self->action_groups = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
+ self->gicons = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+ self->groups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_ptr_array_unref);
+
+ g_signal_connect_object (self->parser, "changed",
+ G_CALLBACK (e_ui_manager_parser_changed_cb), self, 0);
+}
+
+/**
+ * e_ui_manager_new:
+ *
+ * Creates a new #EUIManager. Use g_object_unref() to free it, when no longer needed.
+ *
+ * Returns: (transfer full): a new #EUIManager
+ *
+ * Since: 3.56
+ **/
+EUIManager *
+e_ui_manager_new (void)
+{
+ return g_object_new (E_TYPE_UI_MANAGER, NULL);
+}
+
+/**
+ * e_ui_manager_get_parser:
+ * @self: an #EUIManager
+ *
+ * Gets an #EUIParser associated with the @self. Any UI definitions
+ * are loaded into it and the layouts are read from it.
+ *
+ * Returns: (transfer none): an #EUIParser
+ *
+ * Since: 3.56
+ **/
+EUIParser *
+e_ui_manager_get_parser (EUIManager *self)
+{
+ g_return_val_if_fail (E_IS_UI_MANAGER (self), NULL);
+
+ return self->parser;
+}
+
+/**
+ * e_ui_manager_get_accel_group:
+ * @self: an #EUIManager
+ *
+ * Gets a #GtkAccelGroup associated with the @self. Actions
+ * with configured accelerators register themselves to this accel
+ * group.
+ *
+ * It should be added to a #GtkWindow with gtk_window_add_accel_group().
+ *
+ * Returns: (transfer none): a #GtkAccelGroup
+ *
+ * Since: 3.56
+ **/
+GtkAccelGroup *
+e_ui_manager_get_accel_group (EUIManager *self)
+{
+ g_return_val_if_fail (E_IS_UI_MANAGER (self), NULL);
+
+ return self->accel_group;
+}
+
+/**
+ * e_ui_manager_freeze:
+ * @self: an #EUIManager
+ *
+ * Freezes change notifications on the @self. The function
+ * can be called multiple times, only each call should be
+ * followed by a corresponding e_ui_manager_thaw().
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_manager_freeze (EUIManager *self)
+{
+ g_return_if_fail (E_IS_UI_MANAGER (self));
+ g_return_if_fail (self->frozen + 1 > self->frozen);
+
+ self->frozen++;
+
+ g_signal_emit (self, signals[SIGNAL_FREEZE], 0, NULL);
+}
+
+/**
+ * e_ui_manager_thaw:
+ * @self: an #EUIManager
+ *
+ * Reverts effect of one e_ui_manager_freeze() call.
+ * It's a programming error to call this function when
+ * the @self is not frozen.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_manager_thaw (EUIManager *self)
+{
+ g_return_if_fail (E_IS_UI_MANAGER (self));
+ g_return_if_fail (self->frozen > 0);
+
+ self->frozen--;
+
+ g_signal_emit (self, signals[SIGNAL_THAW], 0, self->changed_while_frozen, NULL);
+
+ if (!self->frozen && self->changed_while_frozen) {
+ self->changed_while_frozen = FALSE;
+
+ e_ui_manager_changed (self);
+ }
+}
+
+/**
+ * e_ui_manager_is_frozen:
+ * @self: an #EUIManager
+ *
+ * Check whether the change notifications are frozen
+ * with e_ui_manager_freeze() call.
+ *
+ * Returns: whether change notifications are frozen
+ *
+ * Since: 3.56
+ **/
+gboolean
+e_ui_manager_is_frozen (EUIManager *self)
+{
+ g_return_val_if_fail (E_IS_UI_MANAGER (self), FALSE);
+
+ return self->frozen > 0;
+}
+
+static gboolean
+e_ui_manager_can_process_accel (EUIManager *self,
+ EUIAction *action)
+{
+ gboolean ignore = FALSE;
+
+ if (!e_ui_action_is_visible (action) ||
+ !g_action_get_enabled (G_ACTION (action))) {
+ return FALSE;
+ }
+
+ g_signal_emit (self, signals[SIGNAL_IGNORE_ACCEL], 0, action, &ignore);
+
+ return !ignore;
+}
+
+typedef struct _EAccelClosure {
+ GClosure parent;
+ EUIManager *manager; /* not referenced */
+} EAccelClosure;
+
+static void
+e_ui_manager_closure_accel_activate (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data)
+{
+ GAction *action = G_ACTION (closure->data);
+ EAccelClosure *accel_closure = (EAccelClosure *) closure;
+
+ if (e_ui_manager_can_process_accel (accel_closure->manager, E_UI_ACTION (action))) {
+ const GVariantType *param_type;
+
+ param_type = g_action_get_parameter_type (action);
+ if (!param_type) {
+ g_action_activate (action, NULL);
+ } else if (param_type == G_VARIANT_TYPE_BOOLEAN) {
+ GVariant *current_value = g_action_get_state (action);
+ GVariant *new_value;
+
+ new_value = g_variant_new_boolean (current_value ? !g_variant_get_boolean (current_value) : TRUE);
+ g_variant_ref_sink (new_value);
+
+ g_action_activate (action, new_value);
+
+ g_clear_pointer (¤t_value, g_variant_unref);
+ g_clear_pointer (&new_value, g_variant_unref);
+ } else {
+ GVariant *target;
+
+ target = e_ui_action_ref_target (E_UI_ACTION (action));
+ g_action_activate (action, target);
+ g_clear_pointer (&target, g_variant_unref);
+ }
+
+ /* accelerator was handled */
+ g_value_set_boolean (return_value, TRUE);
+ }
+}
+
+static void
+e_ui_manager_connect_accel_cb (EUIManager *self,
+ EUIAction *action,
+ const gchar *accel,
+ gpointer user_data)
+{
+ guint key = 0;
+ GdkModifierType mods = 0;
+
+ g_return_if_fail (E_IS_UI_MANAGER (self));
+
+ if (!self->accel_group || !accel || !*accel)
+ return;
+
+ gtk_accelerator_parse (accel, &key, &mods);
+
+ if (key != 0) {
+ GClosure *closure;
+ EAccelClosure *accel_closure;
+
+ closure = g_closure_new_object (sizeof (GClosure), G_OBJECT (action));
+ g_closure_set_marshal (closure, e_ui_manager_closure_accel_activate);
+
+ accel_closure = (EAccelClosure *) closure;
+ accel_closure->manager = self;
+
+ gtk_accel_group_connect (self->accel_group, key, mods, GTK_ACCEL_LOCKED, closure);
+ } else {
+ EUIActionGroup *action_group = e_ui_action_get_action_group (action);
+
+ g_warning ("%s: Failed to parse accel '%s' on action '%s.%s'", G_STRFUNC, accel,
+ action_group ? e_ui_action_group_get_name (action_group) : "no-group-set",
+ g_action_get_name (G_ACTION (action)));
+ }
+}
+
+
+static void
+e_ui_manager_disconnect_accel_cb (EUIManager *self,
+ EUIAction *action,
+ const gchar *accel,
+ gpointer user_data)
+{
+ guint key = 0;
+ GdkModifierType mods = 0;
+
+ g_return_if_fail (E_IS_UI_MANAGER (self));
+
+ if (!self->accel_group || !accel || !*accel)
+ return;
+
+ gtk_accelerator_parse (accel, &key, &mods);
+
+ if (key != 0)
+ gtk_accel_group_disconnect_key (self->accel_group, key, mods);
+}
+
+static void
+e_ui_manager_foreach_action_accel (EUIManager *self,
+ EUIAction *action,
+ void (*func) (EUIManager *self,
+ EUIAction *action,
+ const gchar *accel,
+ gpointer user_data),
+ gpointer user_data)
+{
+ GPtrArray *secondary_accels;
+ const gchar *accel;
+
+ accel = e_ui_action_get_accel (action);
+ if (accel && *accel)
+ func (self, action, accel, user_data);
+
+ secondary_accels = e_ui_action_get_secondary_accels (action);
+ if (secondary_accels) {
+ guint ii;
+
+ for (ii = 0; ii < secondary_accels->len; ii++) {
+ accel = g_ptr_array_index (secondary_accels, ii);
+
+ if (accel && *accel)
+ func (self, action, accel, user_data);
+ }
+ }
+}
+
+static void
+e_ui_manager_add_action_accel (EUIManager *self,
+ EUIAction *action)
+{
+ e_ui_manager_foreach_action_accel (self, action, e_ui_manager_connect_accel_cb, NULL);
+}
+
+static void
+e_ui_manager_add_action_internal (EUIManager *self,
+ EUIActionGroup *action_group,
+ EUIAction *action,
+ EUIActionFunc activate,
+ EUIActionFunc change_state,
+ gpointer user_data)
+{
+ if (activate)
+ g_signal_connect (action, "activate", G_CALLBACK (activate), user_data);
+
+ if (change_state)
+ g_signal_connect (action, "change-state", G_CALLBACK (change_state), user_data);
+
+ /* this calls e_ui_manager_add_action_accel() in the group's "added" signal handler */
+ e_ui_action_group_add (action_group, action);
+}
+
+/**
+ * e_ui_manager_add_actions:
+ * @self: an #EUIManager
+ * @group_name: (not nullable): a name of an action group to add the actions to
+ * @translation_domain: (nullable): translation domain for the @entries' localized members, or %NULL
+ * @entries: action entries to be added, as #EUIActionEntry array
+ * @n_entries: how many items @entries has, or -1 when NULL-terminated
+ * @user_data: user data to pass to action callbacks
+ *
+ * Just like g_action_map_add_action_entries(), only uses #EUIAction instead
+ * of #GSimpleAction actions and adds the actions into a named action group.
+ * When there is no action group of the @group_name, a new is added.
+ *
+ * The @translation_domain can be #NULL, in which case the Evolution's translation
+ * domain is used. It's used also when the @translation_domain is an empty string.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_manager_add_actions (EUIManager *self,
+ const gchar *group_name,
+ const gchar *translation_domain,
+ const EUIActionEntry *entries,
+ gint n_entries,
+ gpointer user_data)
+{
+ EUIActionGroup *action_group;
+ const gchar *domain;
+ guint ii;
+
+ g_return_if_fail (E_IS_UI_MANAGER (self));
+ g_return_if_fail (group_name != NULL);
+ g_return_if_fail (entries != NULL || n_entries == 0);
+
+ domain = translation_domain;
+ if (!domain || !*domain)
+ domain = GETTEXT_PACKAGE;
+
+ action_group = e_ui_manager_get_action_group (self, group_name);
+
+ for (ii = 0; n_entries < 0 ? entries[ii].name != NULL : ii < n_entries; ii++) {
+ const EUIActionEntry *entry = &(entries[ii]);
+ EUIAction *action;
+
+ action = e_ui_action_new_from_entry (group_name, entry, domain);
+ if (!action)
+ continue;
+
+ e_ui_manager_add_action_internal (self, action_group, action, entry->activate, entry->change_state, user_data);
+
+ g_object_unref (action);
+ }
+
+ e_ui_manager_changed (self);
+}
+
+/**
+ * e_ui_manager_add_actions_enum:
+ * @self: an #EUIManager
+ * @group_name: (not nullable): a name of an action group to add the actions to
+ * @translation_domain: (nullable): translation domain for the @entries' localized members, or %NULL
+ * @entries: action entries to be added, as #EUIActionEnumEntry array
+ * @n_entries: how many items @entries has, or -1 when NULL-terminated
+ * @user_data: user data to pass to action callbacks
+ *
+ * The same as e_ui_manager_add_actions(), only creates radio actions, whose
+ * states correspond to certain enum value. If bindings between the action's
+ * state and a corresponding enum #GObject property is needed, a utility function
+ * e_ui_action_util_gvalue_to_enum_state() and e_ui_action_util_enum_state_to_gvalue()
+ * can be used to transform the value from the #GObject to an action state or vice versa.
+ *
+ * The @translation_domain can be %NULL, in which case the Evolution's translation
+ * domain is used. It's used also when the @translation_domain is an empty string.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_manager_add_actions_enum (EUIManager *self,
+ const gchar *group_name,
+ const gchar *translation_domain,
+ const EUIActionEnumEntry *entries,
+ gint n_entries,
+ gpointer user_data)
+{
+ EUIActionGroup *action_group;
+ const gchar *domain;
+ guint ii;
+
+ g_return_if_fail (E_IS_UI_MANAGER (self));
+ g_return_if_fail (group_name != NULL);
+ g_return_if_fail (entries != NULL || n_entries == 0);
+
+ domain = translation_domain;
+ if (!domain || !*domain)
+ domain = GETTEXT_PACKAGE;
+
+ action_group = e_ui_manager_get_action_group (self, group_name);
+
+ for (ii = 0; n_entries < 0 ? entries[ii].name != NULL : ii < n_entries; ii++) {
+ const EUIActionEnumEntry *entry = &(entries[ii]);
+ EUIAction *action;
+
+ action = e_ui_action_new_from_enum_entry (group_name, entry, domain);
+ if (!action)
+ continue;
+
+ e_ui_manager_add_action_internal (self, action_group, action, entry->activate, (EUIActionFunc) e_ui_action_set_state, user_data);
+
+ g_object_unref (action);
+ }
+
+ e_ui_manager_changed (self);
+}
+
+/**
+ * e_ui_manager_add_action:
+ * @self: an #EUIManager
+ * @group_name: (not nullable): a name of an action group to add the action to
+ * @action: (transfer none): the action to add
+ * @activate: (nullable): an optional callback to call on "activate" signal, or %NULL
+ * @change_state: (nullable): an optional callback to call on "change-state" signal, or %NULL
+ * @user_data: user data to pass to action callbacks
+ *
+ * Adds a single @action to the @group_name. The actions group cannot contain
+ * an action of the same name.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_manager_add_action (EUIManager *self,
+ const gchar *group_name,
+ EUIAction *action,
+ EUIActionFunc activate,
+ EUIActionFunc change_state,
+ gpointer user_data)
+{
+ EUIActionGroup *action_group;
+
+ g_return_if_fail (E_IS_UI_MANAGER (self));
+ g_return_if_fail (group_name != NULL);
+ g_return_if_fail (E_IS_UI_ACTION (action));
+
+ action_group = e_ui_manager_get_action_group (self, group_name);
+ e_ui_manager_add_action_internal (self, action_group, action, activate, change_state, user_data);
+
+ e_ui_manager_changed (self);
+}
+
+/**
+ * e_ui_manager_add_actions_with_eui_data:
+ * @self: an #EUIManager
+ * @group_name: (not nullable): a name of an action group to add the actions to
+ * @translation_domain: (nullable): translation domain for the @entries' localized members, or %NULL
+ * @entries: action entries to be added, as #EUIActionEntry array
+ * @n_entries: how many items @entries has, or -1 when NULL-terminated
+ * @user_data: user data to pass to action callbacks
+ * @eui: Evolution UI definition
+ *
+ * Combines e_ui_manager_add_actions() with e_ui_parser_merge_data(), in this order,
+ * printing any errors in the @eui into the terminal. This can be used to add built-in
+ * UI definition with the related actions in a single call.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_manager_add_actions_with_eui_data (EUIManager *self,
+ const gchar *group_name,
+ const gchar *translation_domain,
+ const EUIActionEntry *entries,
+ gint n_entries,
+ gpointer user_data,
+ const gchar *eui)
+{
+ GError *local_error = NULL;
+
+ g_return_if_fail (E_IS_UI_MANAGER (self));
+ g_return_if_fail (group_name != NULL);
+ g_return_if_fail (entries != NULL || n_entries == 0);
+ g_return_if_fail (eui != NULL);
+
+ e_ui_manager_add_actions (self, group_name, translation_domain, entries, n_entries, user_data);
+
+ if (!e_ui_parser_merge_data (e_ui_manager_get_parser (self), eui, -1, &local_error))
+ g_critical ("%s: Failed to merge built-in UI definition: %s", G_STRFUNC, local_error ? local_error->message : "Unknown error");
+
+ g_clear_error (&local_error);
+}
+
+/**
+ * e_ui_manager_add_action_groups_to_widget:
+ * @self: an #EUIManager
+ * @widget: a #GtkWidget
+ *
+ * Adds all currently known action groups into the @widget with
+ * gtk_widget_insert_action_group(). In contrast to e_ui_manager_set_action_groups_widget(),
+ * this does not add any later added action groups into the @widget.
+ * In other words, this is a one-time operation only.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_manager_add_action_groups_to_widget (EUIManager *self,
+ GtkWidget *widget)
+{
+ GHashTableIter iter;
+ gpointer key = NULL, value = NULL;
+
+ g_return_if_fail (E_IS_UI_MANAGER (self));
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ g_hash_table_iter_init (&iter, self->action_groups);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ const gchar *name = key;
+ EUIActionGroup *group = value;
+
+ gtk_widget_insert_action_group (widget, name, G_ACTION_GROUP (group));
+ }
+}
+
+/**
+ * e_ui_manager_set_action_groups_widget:
+ * @self: an #EUIManager
+ * @widget: (nullable): a #GtkWidget, or %NULL
+ *
+ * Sets the @widget to be the one where all action groups will
+ * be inserted. When the @self creates a new action group, it
+ * is automatically added into the @widget.
+ *
+ * When the @widget is %NULL, any previous widget is unset.
+ *
+ * Overwriting any existing @widget will remove the groups
+ * from the previous widget first.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_manager_set_action_groups_widget (EUIManager *self,
+ GtkWidget *widget)
+{
+ GtkWidget *current;
+ GHashTableIter iter;
+ gpointer key = NULL, value = NULL;
+
+ g_return_if_fail (E_IS_UI_MANAGER (self));
+ if (widget)
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ current = g_weak_ref_get (&self->action_groups_widget);
+
+ if (current == widget) {
+ g_clear_object (¤t);
+ return;
+ }
+
+ g_hash_table_iter_init (&iter, self->action_groups);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ const gchar *name = key;
+ EUIActionGroup *group = value;
+
+ if (current)
+ gtk_widget_insert_action_group (current, name, NULL);
+
+ if (widget)
+ gtk_widget_insert_action_group (widget, name, G_ACTION_GROUP (group));
+ }
+
+ g_weak_ref_set (&self->action_groups_widget, widget);
+
+ g_clear_object (¤t);
+}
+
+/**
+ * e_ui_manager_ref_action_groups_widget:
+ * @self: an #EUIManager
+ *
+ * References a #GtkWidget, which is used to add action groups to.
+ *
+ * The returned widget, if not %NULL, should be dereferenced
+ * with g_object_unref(), when no longer needed.
+ *
+ * Returns: (transfer full) (nullable): a referenced #GtkWidget used
+ * to add action groups to, or %NULL, when none is set
+ *
+ * Since: 3.56
+ **/
+GtkWidget *
+e_ui_manager_ref_action_groups_widget (EUIManager *self)
+{
+ g_return_val_if_fail (E_IS_UI_MANAGER (self), NULL);
+
+ return g_weak_ref_get (&self->action_groups_widget);
+}
+
+/**
+ * e_ui_manager_has_action_group:
+ * @self: an #EUIManager
+ * @name: an action group name
+ *
+ * Check whether an action group named @name already exists in @self.
+ *
+ * Returns: %TRUE, when the action group exists, %FALSE otherwise
+ *
+ * Since: 3.56
+ **/
+gboolean
+e_ui_manager_has_action_group (EUIManager *self,
+ const gchar *name)
+{
+ g_return_val_if_fail (E_IS_UI_MANAGER (self), FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ return g_hash_table_lookup (self->action_groups, name) != NULL;
+}
+
+static void
+e_ui_manager_action_group_action_added_cb (EUIActionGroup *action_group,
+ EUIAction *action,
+ gpointer user_data)
+{
+ EUIManager *self = user_data;
+
+ e_ui_manager_add_action_accel (self, action);
+}
+
+static void
+e_ui_manager_action_group_action_removed_cb (EUIActionGroup *action_group,
+ EUIAction *action,
+ gpointer user_data)
+{
+ EUIManager *self = user_data;
+
+ e_ui_manager_foreach_action_accel (self, action, e_ui_manager_disconnect_accel_cb, NULL);
+}
+
+static void
+e_ui_manager_claim_new_action_group (EUIManager *self,
+ EUIActionGroup *action_group) /* (transfer full) */
+{
+ GtkWidget *action_groups_widget;
+ GPtrArray *actions;
+ const gchar *name;
+ guint ii;
+
+ name = e_ui_action_group_get_name (action_group);
+
+ g_hash_table_insert (self->action_groups, (gpointer) name, action_group);
+
+ actions = e_ui_action_group_list_actions (action_group);
+ for (ii = 0; ii < actions->len; ii++) {
+ EUIAction *action = g_ptr_array_index (actions, ii);
+
+ e_ui_manager_add_action_accel (self, action);
+ }
+
+ g_clear_pointer (&actions, g_ptr_array_unref);
+
+ action_groups_widget = g_weak_ref_get (&self->action_groups_widget);
+ if (action_groups_widget) {
+ gtk_widget_insert_action_group (action_groups_widget, name, G_ACTION_GROUP (action_group));
+ g_clear_object (&action_groups_widget);
+ }
+
+ g_signal_connect_object (action_group, "added",
+ G_CALLBACK (e_ui_manager_action_group_action_added_cb), self, 0);
+ g_signal_connect_object (action_group, "removed",
+ G_CALLBACK (e_ui_manager_action_group_action_removed_cb), self, 0);
+ g_signal_connect_object (action_group, "accel-added",
+ G_CALLBACK (e_ui_manager_connect_accel_cb), self, G_CONNECT_SWAPPED);
+ g_signal_connect_object (action_group, "accel-removed",
+ G_CALLBACK (e_ui_manager_disconnect_accel_cb), self, G_CONNECT_SWAPPED);
+}
+
+/**
+ * e_ui_manager_get_action_group:
+ * @self: an #EUIManager
+ * @name: an action group name
+ *
+ * Gets an #EUIActionGroup named @name. If no such exists, a new is created.
+ * Use e_ui_manager_has_action_group() to check whether an action group exists.
+ *
+ * Returns: (transfer none): an #EUIActionGroup named @name
+ *
+ * Since: 3.56
+ **/
+EUIActionGroup *
+e_ui_manager_get_action_group (EUIManager *self,
+ const gchar *name)
+{
+ EUIActionGroup *action_group;
+
+ g_return_val_if_fail (E_IS_UI_MANAGER (self), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ action_group = g_hash_table_lookup (self->action_groups, name);
+ if (!action_group) {
+ action_group = e_ui_action_group_new (name);
+
+ e_ui_manager_claim_new_action_group (self, action_group);
+ }
+
+ return action_group;
+}
+
+/**
+ * e_ui_manager_add_action_group:
+ * @self: an #EUIManager
+ * @action_group: (transfer none): an #EUIActionGroup to add
+ *
+ * Adds an existing action group to the @self, being able to
+ * call actions from it. The function does nothing when the @action_group
+ * is already part of the @self, nonetheless it's considered a programming
+ * error to try to add a different group of the same name into the @self.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_manager_add_action_group (EUIManager *self,
+ EUIActionGroup *action_group)
+{
+ EUIActionGroup *existing_group;
+
+ g_return_if_fail (E_IS_UI_MANAGER (self));
+ g_return_if_fail (E_IS_UI_ACTION_GROUP (action_group));
+
+ existing_group = g_hash_table_lookup (self->action_groups, e_ui_action_group_get_name (action_group));
+
+ if (existing_group && existing_group != action_group) {
+ g_warning ("%s: A different action group of the name '%s' already exists, ignoring the new group",
+ G_STRFUNC, e_ui_action_group_get_name (action_group));
+ return;
+ } else if (existing_group == action_group) {
+ return;
+ }
+
+ e_ui_manager_claim_new_action_group (self, g_object_ref (action_group));
+}
+
+/**
+ * e_ui_manager_get_action:
+ * @self: an #EUIManager
+ * @name: an action name
+ *
+ * Looks up an #EUIAction by its @name among all known action maps and returns it.
+ *
+ * Returns: (nullable) (transfer none): an #EUIAction named @name, or %NULL, if not found
+ *
+ * Since: 3.56
+ **/
+EUIAction *
+e_ui_manager_get_action (EUIManager *self,
+ const gchar *name)
+{
+ GHashTableIter iter;
+ gpointer key = NULL, value = NULL;
+
+ g_return_val_if_fail (E_IS_UI_MANAGER (self), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ g_hash_table_iter_init (&iter, self->action_groups);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ const gchar *group_name = key;
+ EUIActionGroup *action_group = value;
+
+ if (action_group) {
+ EUIAction *action;
+
+ action = e_ui_action_group_get_action (action_group, name);
+ if (action) {
+ if (E_IS_UI_ACTION (action))
+ return action;
+
+ g_warning ("%s: Found action '%s' in action group '%s', but it's not an EUIAction, it's %s instead",
+ G_STRFUNC, name, group_name, G_OBJECT_TYPE_NAME (action));
+ break;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * e_ui_manager_get_gicon:
+ * @self: an #EUIManager
+ * @name: a #GIcon name to retrieve
+ *
+ * Retrieves a named #GIcon. These are cached. The signal "create-gicon"
+ * is used to create icons prefixed with "gicon::" in the .eui definitions,
+ * as they are custom icons. The @name is without this prefix.
+ *
+ * While returning %NULL is possible, it's considered an error and
+ * a runtime warning is printed on the terminal, thus there should
+ * always be a "create-gicon" signal handler providing the #GIcon.
+ *
+ * Returns: (transfer none) (nullable): a #GIcon named @name, or %NULL, if not found
+ *
+ * Since: 3.56
+ **/
+GIcon *
+e_ui_manager_get_gicon (EUIManager *self,
+ const gchar *name)
+{
+ GIcon *gicon = NULL;
+
+ g_return_val_if_fail (E_IS_UI_MANAGER (self), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ gicon = g_hash_table_lookup (self->gicons, name);
+
+ if (!gicon) {
+ gboolean handled = FALSE;
+
+ g_signal_emit (self, signals[SIGNAL_CREATE_GICON], 0, name, &gicon, &handled);
+
+ if (!gicon) {
+ g_warning ("%s: Nothing created gicon '%s'", G_STRFUNC, name);
+ gicon = g_themed_icon_new ("image-missing");
+ }
+
+ g_hash_table_insert (self->gicons, g_strdup (name), gicon);
+ }
+
+ return gicon;
+}
+
+static gboolean
+eum_parent_is_header_bar_button (GtkWidget *child)
+{
+ while (child && !E_IS_HEADER_BAR_BUTTON (child))
+ child = gtk_widget_get_parent (child);
+
+ return child != NULL;
+}
+
+static void
+e_ui_manager_add_css_classes (EUIManager *self,
+ GtkWidget *item,
+ const gchar *css_classes) /* space-separated */
+{
+ EHeaderBarButton *header_bar_button = NULL;
+ GtkStyleContext *style_context;
+
+ if (!item || !css_classes || !*css_classes)
+ return;
+
+ header_bar_button = E_IS_HEADER_BAR_BUTTON (item) ? E_HEADER_BAR_BUTTON (item) : NULL;
+ style_context = gtk_widget_get_style_context (item);
+
+ if (strchr (css_classes, ' ')) {
+ gchar **strv = g_strsplit (css_classes, " ", -1);
+ guint ii;
+
+ for (ii = 0; strv && strv[ii]; ii++) {
+ gchar *css_class = g_strchomp (strv[ii]);
+
+ if (*css_class) {
+ if (header_bar_button)
+ e_header_bar_button_css_add_class (header_bar_button, css_class);
+ else
+ gtk_style_context_add_class (style_context, css_class);
+ }
+ }
+
+ g_strfreev (strv);
+ } else {
+ if (header_bar_button)
+ e_header_bar_button_css_add_class (header_bar_button, css_classes);
+ else
+ gtk_style_context_add_class (style_context, css_classes);
+ }
+}
+
+static GObject *
+e_ui_manager_create_headerbar_item (EUIManager *self,
+ EUIElement *elem,
+ EUIAction *action)
+{
+ GtkWidget *item;
+
+ item = e_header_bar_button_new (e_ui_action_get_label (action), action, self);
+
+ if (e_ui_element_item_get_icon_only_is_set (elem))
+ e_header_bar_button_set_show_icon_only (E_HEADER_BAR_BUTTON (item), e_ui_element_item_get_icon_only (elem));
+
+ e_ui_manager_add_css_classes (self, item, e_ui_element_item_get_css_classes (elem));
+ e_ui_manager_update_item_from_action (self, item, action);
+
+ return G_OBJECT (item);
+}
+
+static GObject *
+e_ui_manager_create_toolbar_item (EUIManager *self,
+ EUIElement *elem,
+ EUIAction *action)
+{
+ GtkToolButton *item = NULL;
+ GVariant *state;
+
+ state = g_action_get_state (G_ACTION (action));
+
+ if (e_ui_action_get_radio_group (action))
+ item = GTK_TOOL_BUTTON (gtk_toggle_tool_button_new ());
+ else if (state && g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
+ item = GTK_TOOL_BUTTON (gtk_toggle_tool_button_new ());
+ else
+ item = GTK_TOOL_BUTTON (gtk_tool_button_new (NULL, NULL));
+
+ g_clear_pointer (&state, g_variant_unref);
+
+ e_ui_manager_add_css_classes (self, GTK_WIDGET (item), e_ui_element_item_get_css_classes (elem));
+ gtk_tool_item_set_is_important (GTK_TOOL_ITEM (item), e_ui_element_item_get_important (elem));
+ e_ui_manager_update_item_from_action (self, item, action);
+
+ return G_OBJECT (item);
+}
+
+static GObject *
+e_ui_manager_create_menu_item (EUIManager *self,
+ EUIElement *elem,
+ EUIAction *action)
+{
+ GMenuItem *item;
+
+ item = g_menu_item_new (NULL, NULL);
+
+ e_ui_manager_update_item_from_action (self, item, action);
+
+ if (e_ui_element_item_get_text_only_is_set (elem) &&
+ e_ui_element_item_get_text_only (elem))
+ g_menu_item_set_attribute (item, "icon", NULL);
+
+ return G_OBJECT (item);
+}
+
+static GObject *
+e_ui_manager_create_item_one (EUIManager *self,
+ EUIElement *elem,
+ EUIAction *action,
+ EUIElementKind for_kind)
+{
+ GObject *item = NULL;
+ gboolean handled = FALSE;
+
+ g_return_val_if_fail (elem != NULL, NULL);
+ g_return_val_if_fail (action != NULL, NULL);
+ g_return_val_if_fail (for_kind == E_UI_ELEMENT_KIND_MENU || for_kind == E_UI_ELEMENT_KIND_HEADERBAR || for_kind == E_UI_ELEMENT_KIND_TOOLBAR, NULL);
+
+ g_signal_emit (self, signals[SIGNAL_CREATE_ITEM], 0, elem, action, for_kind, &item, &handled);
+
+ if (!handled && !item) {
+ switch (for_kind) {
+ case E_UI_ELEMENT_KIND_HEADERBAR:
+ item = e_ui_manager_create_headerbar_item (self, elem, action);
+ break;
+ case E_UI_ELEMENT_KIND_TOOLBAR:
+ item = e_ui_manager_create_toolbar_item (self, elem, action);
+ break;
+ case E_UI_ELEMENT_KIND_MENU:
+ item = e_ui_manager_create_menu_item (self, elem, action);
+ break;
+ default:
+ g_warn_if_reached ();
+ break;
+ }
+ }
+
+ return item;
+}
+
+static void
+emu_traverse_headerbar_rec (EUIManager *self,
+ EHeaderBar *eheaderbar,
+ GtkHeaderBar *gtkheaderbar,
+ EUIElement *parent_elem,
+ gboolean add_to_start,
+ GHashTable *groups,
+ gboolean *inout_need_separator,
+ gboolean *inout_any_added)
+{
+ guint ii, len;
+
+ len = e_ui_element_get_n_children (parent_elem);
+
+ for (ii = 0; ii < len; ii++) {
+ EUIElement *elem = e_ui_element_get_child (parent_elem, add_to_start ? ii : len - ii - 1);
+ EUIAction *action;
+
+ if (!elem)
+ continue;
+
+ switch (e_ui_element_get_kind (elem)) {
+ case E_UI_ELEMENT_KIND_ITEM:
+ action = e_ui_manager_get_action (self, e_ui_element_item_get_action (elem));
+ if (action) {
+ GObject *item;
+
+ item = e_ui_manager_create_item_one (self, elem, action, E_UI_ELEMENT_KIND_HEADERBAR);
+ if (item) {
+ GPtrArray *radio_group;
+
+ if (*inout_any_added && *inout_need_separator) {
+ GtkWidget *separator;
+
+ separator = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
+ gtk_widget_set_visible (separator, TRUE);
+
+ if (eheaderbar) {
+ if (add_to_start)
+ e_header_bar_pack_start (eheaderbar, separator, 0);
+ else
+ e_header_bar_pack_end (eheaderbar, separator, 0);
+ } else {
+ if (add_to_start)
+ gtk_header_bar_pack_start (gtkheaderbar, separator);
+ else
+ gtk_header_bar_pack_end (gtkheaderbar, separator);
+ }
+ }
+
+ *inout_any_added = TRUE;
+ *inout_need_separator = FALSE;
+
+ radio_group = e_ui_action_get_radio_group (action);
+ if (radio_group && GTK_IS_RADIO_BUTTON (item)) {
+ GSList **pgroup;
+
+ pgroup = g_hash_table_lookup (groups, radio_group);
+ if (pgroup) {
+ gtk_radio_button_set_group (GTK_RADIO_BUTTON (item), *pgroup);
+ } else {
+ pgroup = g_new0 (GSList *, 1);
+ *pgroup = gtk_radio_button_get_group (GTK_RADIO_BUTTON (item));
+ g_hash_table_insert (groups, radio_group, pgroup);
+ }
+ }
+
+ if (eheaderbar) {
+ if (add_to_start)
+ e_header_bar_pack_start (eheaderbar, GTK_WIDGET (item), e_ui_element_item_get_label_priority (elem));
+ else
+ e_header_bar_pack_end (eheaderbar, GTK_WIDGET (item), e_ui_element_item_get_label_priority (elem));
+ } else {
+ if (add_to_start)
+ gtk_header_bar_pack_start (gtkheaderbar, GTK_WIDGET (item));
+ else
+ gtk_header_bar_pack_end (gtkheaderbar, GTK_WIDGET (item));
+ }
+ }
+ } else {
+ g_warning ("%s: Cannot find action '%s' for an item", G_STRFUNC, e_ui_element_item_get_action (elem));
+ }
+ break;
+ case E_UI_ELEMENT_KIND_SEPARATOR:
+ *inout_need_separator = *inout_any_added;
+ break;
+ case E_UI_ELEMENT_KIND_PLACEHOLDER:
+ emu_traverse_headerbar_rec (self, eheaderbar, gtkheaderbar, elem, add_to_start, groups, inout_need_separator, inout_any_added);
+ break;
+ default:
+ g_warn_if_reached ();
+ break;
+ }
+ }
+}
+
+static void
+emu_traverse_headerbar (EUIManager *self,
+ EHeaderBar *eheaderbar,
+ GtkHeaderBar *gtkheaderbar,
+ EUIElement *elem)
+{
+ GHashTable *groups; /* gpointer group ~> GSList ** */
+ gboolean need_separator = FALSE;
+ gboolean any_added = FALSE;
+ guint ii, len;
+
+ groups = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
+
+ len = e_ui_element_get_n_children (elem);
+ for (ii = 0; ii < len; ii++) {
+ EUIElement *subelem = e_ui_element_get_child (elem, ii);
+
+ if (e_ui_element_get_kind (subelem) == E_UI_ELEMENT_KIND_START ||
+ e_ui_element_get_kind (subelem) == E_UI_ELEMENT_KIND_END) {
+ emu_traverse_headerbar_rec (self, eheaderbar, gtkheaderbar, subelem,
+ e_ui_element_get_kind (subelem) == E_UI_ELEMENT_KIND_START,
+ groups, &need_separator, &any_added);
+ } else {
+ g_warn_if_reached ();
+ }
+ }
+
+ g_hash_table_destroy (groups);
+}
+
+static void
+emu_headerbar_handle_changed_cb (EUIManager *self,
+ gpointer user_data)
+{
+ EHeaderBar *headerbar = user_data;
+ EUIElement *elem;
+ const gchar *id;
+
+ g_return_if_fail (E_IS_UI_MANAGER (self));
+ g_return_if_fail (E_IS_HEADER_BAR (headerbar));
+
+ e_header_bar_remove_all (headerbar);
+
+ id = gtk_widget_get_name (GTK_WIDGET (headerbar));
+ g_return_if_fail (id != NULL);
+ g_return_if_fail (e_ui_parser_get_root (self->parser) != NULL);
+
+ elem = e_ui_element_get_child_by_id (e_ui_parser_get_root (self->parser), id);
+ if (!elem) {
+ g_warning ("%s: Cannot find item with id '%s'", G_STRFUNC, id);
+ return;
+ }
+
+ emu_traverse_headerbar (self, headerbar, NULL, elem);
+}
+
+static GObject *
+e_ui_manager_create_item_for_headerbar (EUIManager *self,
+ EUIElement *elem)
+{
+ GtkWidget *widget;
+
+ g_return_val_if_fail (elem != NULL, NULL);
+
+ if (e_ui_element_headerbar_get_use_gtk_type (elem)) {
+ GtkHeaderBar *gtkheaderbar;
+
+ widget = gtk_header_bar_new ();
+ gtkheaderbar = GTK_HEADER_BAR (widget);
+
+ gtk_header_bar_set_show_close_button (gtkheaderbar, TRUE);
+
+ emu_traverse_headerbar (self, NULL, gtkheaderbar, elem);
+ } else {
+ EHeaderBar *eheaderbar;
+
+ widget = e_header_bar_new ();
+ eheaderbar = E_HEADER_BAR (widget);
+
+ g_signal_connect_object (self, "changed",
+ G_CALLBACK (emu_headerbar_handle_changed_cb), eheaderbar, 0);
+
+ emu_traverse_headerbar (self, eheaderbar, NULL, elem);
+ }
+
+ gtk_widget_set_name (widget, e_ui_element_get_id (elem));
+
+ return G_OBJECT (widget);
+}
+
+static void
+emu_traverse_toolbar_rec (EUIManager *self,
+ GtkToolbar *toolbar,
+ EUIElement *parent_elem,
+ GHashTable *groups,
+ gboolean *inout_need_separator,
+ gboolean *inout_any_added)
+{
+ guint ii, len;
+
+ len = e_ui_element_get_n_children (parent_elem);
+
+ for (ii = 0; ii < len; ii++) {
+ EUIElement *elem = e_ui_element_get_child (parent_elem, ii);
+ EUIAction *action;
+
+ if (!elem)
+ continue;
+
+ switch (e_ui_element_get_kind (elem)) {
+ case E_UI_ELEMENT_KIND_ITEM:
+ action = e_ui_manager_get_action (self, e_ui_element_item_get_action (elem));
+ if (action) {
+ GObject *item;
+
+ item = e_ui_manager_create_item_one (self, elem, action, E_UI_ELEMENT_KIND_TOOLBAR);
+ if (item) {
+ if (GTK_IS_TOOL_ITEM (item)) {
+ if (*inout_any_added && *inout_need_separator) {
+ GtkToolItem *separator;
+
+ separator = gtk_separator_tool_item_new ();
+ gtk_toolbar_insert (toolbar, separator, -1);
+ }
+
+ *inout_any_added = TRUE;
+ *inout_need_separator = FALSE;
+
+ gtk_toolbar_insert (toolbar, GTK_TOOL_ITEM (item), -1);
+ } else {
+ g_warning ("%s: Expected GtkToolItem, but received %s for action '%s.%s'", G_STRFUNC,
+ G_OBJECT_TYPE_NAME (item), e_ui_action_get_map_name (action), e_ui_element_item_get_action (elem));
+ g_object_ref_sink (item);
+ g_clear_object (&item);
+ }
+ }
+ } else {
+ g_warning ("%s: Cannot find action '%s' for an item", G_STRFUNC, e_ui_element_item_get_action (elem));
+ }
+ break;
+ case E_UI_ELEMENT_KIND_SEPARATOR:
+ *inout_need_separator = *inout_any_added;
+ break;
+ case E_UI_ELEMENT_KIND_PLACEHOLDER:
+ emu_traverse_toolbar_rec (self, toolbar, elem, groups, inout_need_separator, inout_any_added);
+ break;
+ default:
+ g_warn_if_reached ();
+ break;
+ }
+ }
+}
+
+static void
+emu_traverse_toolbar (EUIManager *self,
+ GtkToolbar *toolbar,
+ EUIElement *elem)
+{
+ GHashTable *groups; /* gpointer group ~> GSList ** */
+ gboolean need_separator = FALSE;
+ gboolean any_added = FALSE;
+
+ groups = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
+
+ emu_traverse_toolbar_rec (self, toolbar, elem, groups, &need_separator, &any_added);
+
+ g_hash_table_destroy (groups);
+}
+
+static void
+emu_toolbar_handle_changed_cb (EUIManager *self,
+ gpointer user_data)
+{
+ GtkContainer *toolbar = user_data;
+ GList *children, *link;
+ EUIElement *elem;
+ const gchar *id;
+
+ g_return_if_fail (E_IS_UI_MANAGER (self));
+ g_return_if_fail (GTK_IS_TOOLBAR (toolbar));
+
+ children = gtk_container_get_children (toolbar);
+ for (link = children; link; link = g_list_next (link)) {
+ gtk_container_remove (toolbar, link->data);
+ }
+
+ g_list_free (children);
+
+ id = gtk_widget_get_name (GTK_WIDGET (toolbar));
+ g_return_if_fail (id != NULL);
+ g_return_if_fail (e_ui_parser_get_root (self->parser) != NULL);
+
+ elem = e_ui_element_get_child_by_id (e_ui_parser_get_root (self->parser), id);
+ if (!elem) {
+ g_warning ("%s: Cannot find item with id '%s'", G_STRFUNC, id);
+ return;
+ }
+
+ emu_traverse_toolbar (self, GTK_TOOLBAR (toolbar), elem);
+}
+
+static GObject *
+e_ui_manager_create_item_for_toolbar (EUIManager *self,
+ EUIElement *elem)
+{
+ GtkToolbar *toolbar;
+
+ g_return_val_if_fail (elem != NULL, NULL);
+
+ toolbar = GTK_TOOLBAR (gtk_toolbar_new ());
+ gtk_widget_set_name (GTK_WIDGET (toolbar), e_ui_element_get_id (elem));
+
+ if (e_ui_element_toolbar_get_primary (elem)) {
+ gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (toolbar)),
+ GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
+ }
+
+ e_util_setup_toolbar_icon_size (toolbar, GTK_ICON_SIZE_BUTTON);
+
+ g_signal_connect_object (self, "changed",
+ G_CALLBACK (emu_toolbar_handle_changed_cb), toolbar, 0);
+
+ emu_traverse_toolbar (self, toolbar, elem);
+
+ return G_OBJECT (toolbar);
+}
+
+static void
+eum_traverse_menu (EUIManager *self,
+ EUIMenu *ui_menu,
+ EUIElement *parent_elem,
+ GMenu *parent_menu,
+ gboolean is_popup,
+ GMenu **inout_section)
+{
+ GMenu *section = *inout_section;
+ guint ii, len;
+
+ len = e_ui_element_get_n_children (parent_elem);
+
+ for (ii = 0; ii < len; ii++) {
+ EUIElement *elem = e_ui_element_get_child (parent_elem, ii);
+ EUIAction *action;
+
+ if (!elem)
+ continue;
+
+ switch (e_ui_element_get_kind (elem)) {
+ case E_UI_ELEMENT_KIND_SUBMENU:
+ action = e_ui_manager_get_action (self, e_ui_element_submenu_get_action (elem));
+ if (action) {
+ e_ui_menu_track_action (ui_menu, action);
+
+ if (e_ui_action_is_visible (action) &&
+ (!is_popup || g_action_get_enabled (G_ACTION (action)))) {
+ GMenu *submenu;
+ GMenu *subsection = NULL;
+
+ submenu = g_menu_new ();
+
+ eum_traverse_menu (self, ui_menu, elem, submenu, is_popup, &subsection);
+
+ if (subsection && g_menu_model_get_n_items (G_MENU_MODEL (subsection)) > 0)
+ g_menu_append_section (submenu, NULL, G_MENU_MODEL (subsection));
+
+ g_clear_object (&subsection);
+
+ if (g_menu_model_get_n_items (G_MENU_MODEL (submenu)) > 0) {
+ GMenuItem *item;
+
+ if (!section)
+ section = g_menu_new ();
+
+ item = g_menu_item_new_submenu (e_ui_action_get_label (action), G_MENU_MODEL (submenu));
+ e_ui_manager_update_item_from_action (self, item, action);
+
+ g_menu_append_item (section, item);
+
+ g_clear_object (&item);
+ }
+
+ g_clear_object (&submenu);
+ }
+ } else {
+ g_warning ("%s: Cannot find action '%s' for a submenu", G_STRFUNC, e_ui_element_submenu_get_action (elem));
+ }
+ break;
+ case E_UI_ELEMENT_KIND_ITEM:
+ action = e_ui_manager_get_action (self, e_ui_element_item_get_action (elem));
+ if (action) {
+ e_ui_menu_track_action (ui_menu, action);
+
+ if (e_ui_action_is_visible (action) &&
+ (!is_popup || g_action_get_enabled (G_ACTION (action)))) {
+ GObject *item;
+
+ item = e_ui_manager_create_item_one (self, elem, action, E_UI_ELEMENT_KIND_MENU);
+ if (item) {
+ if (G_IS_MENU_ITEM (item)) {
+ if (!section)
+ section = g_menu_new ();
+ g_menu_append_item (section, G_MENU_ITEM (item));
+ } else {
+ g_warning ("%s: Expected GMenuItem, but received %s for action '%s.%s'", G_STRFUNC,
+ G_OBJECT_TYPE_NAME (item), e_ui_action_get_map_name (action), e_ui_element_item_get_action (elem));
+ }
+ g_clear_object (&item);
+ }
+ }
+ } else {
+ g_warning ("%s: Cannot find action '%s' for an item", G_STRFUNC, e_ui_element_item_get_action (elem));
+ }
+ break;
+ case E_UI_ELEMENT_KIND_SEPARATOR:
+ if (section && g_menu_model_get_n_items (G_MENU_MODEL (section)) > 0) {
+ if (parent_menu)
+ g_menu_append_section (parent_menu, NULL, G_MENU_MODEL (section));
+ else
+ e_ui_menu_append_section (ui_menu, G_MENU_MODEL (section));
+ }
+
+ g_clear_object (§ion);
+ break;
+ case E_UI_ELEMENT_KIND_PLACEHOLDER:
+ eum_traverse_menu (self, ui_menu, elem, parent_menu, is_popup, §ion);
+ break;
+ default:
+ g_warn_if_reached ();
+ break;
+ }
+ }
+
+ *inout_section = section;
+}
+
+static GObject *
+e_ui_manager_create_item_for_menu (EUIManager *self,
+ EUIElement *elem)
+{
+ EUIMenu *ui_menu;
+ guint ii;
+
+ g_return_val_if_fail (elem != NULL, NULL);
+
+ ui_menu = e_ui_menu_new (self, e_ui_element_get_id (elem));
+
+ /* to match the freeze count, because the ui_menu listens for both "freeze"
+ and "thaw" signals where it will receive only the "thaw" signal without
+ the corresponding "freeze" signal for this frozen count */
+ for (ii = 0; ii < self->frozen; ii++) {
+ e_ui_menu_freeze (ui_menu);
+ }
+
+ return G_OBJECT (ui_menu);
+}
+
+/**
+ * e_ui_manager_create_item:
+ * @self: an #EUIManager
+ * @id: an identifier of the toplevel item
+ *
+ * Creates a new item corresponding to the identifier @id. This
+ * is supposed to be part of the @self's #EUIParser on the toplevel,
+ * thus either a menu, a headerbar or a toolbar. It's an error to ask
+ * for an item which does not exist.
+ *
+ * The returned #GObject is an #EUIMenu (a #GMenuModel descendant) for the menu,
+ * an #EHeaderBar for the headerbar and a #GtkToolbar for the toolbar.
+ *
+ * Returns: (transfer full): a new #GObject for the @id
+ *
+ * Since: 3.56
+ **/
+GObject *
+e_ui_manager_create_item (EUIManager *self,
+ const gchar *id)
+{
+ EUIElement *elem;
+ GObject *object = NULL;
+
+ g_return_val_if_fail (E_IS_UI_MANAGER (self), NULL);
+ g_return_val_if_fail (id != NULL, NULL);
+ g_return_val_if_fail (e_ui_parser_get_root (self->parser) != NULL, NULL);
+
+ elem = e_ui_element_get_child_by_id (e_ui_parser_get_root (self->parser), id);
+ if (!elem) {
+ g_warning ("%s: Cannot find item with id '%s'", G_STRFUNC, id);
+ return NULL;
+ }
+
+ switch (e_ui_element_get_kind (elem)) {
+ case E_UI_ELEMENT_KIND_HEADERBAR:
+ object = e_ui_manager_create_item_for_headerbar (self, elem);
+ break;
+ case E_UI_ELEMENT_KIND_TOOLBAR:
+ object = e_ui_manager_create_item_for_toolbar (self, elem);
+ break;
+ case E_UI_ELEMENT_KIND_MENU:
+ object = e_ui_manager_create_item_for_menu (self, elem);
+ break;
+ default:
+ g_warn_if_reached ();
+ break;
+ }
+
+ if (GTK_IS_WIDGET (object))
+ gtk_widget_set_visible (GTK_WIDGET (object), TRUE);
+
+ return object;
+}
+
+/**
+ * e_ui_manager_fill_menu:
+ * @self: an #EUIManager
+ * @id: a menu ID to fill the @ui_menu with
+ * @ui_menu: an #EUIMenu to be filled
+ *
+ * Fills the @ui_menu with the menu definition with ID @id.
+ * This is meant to be used by #EUIMenu itself during its rebuild.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_manager_fill_menu (EUIManager *self,
+ const gchar *id,
+ EUIMenu *ui_menu)
+{
+ EUIElement *elem;
+ GMenu *section = NULL;
+
+ g_return_if_fail (E_IS_UI_MANAGER (self));
+ g_return_if_fail (id != NULL);
+ g_return_if_fail (E_IS_UI_MENU (ui_menu));
+ g_return_if_fail (e_ui_parser_get_root (self->parser) != NULL);
+
+ elem = e_ui_element_get_child_by_id (e_ui_parser_get_root (self->parser), id);
+ if (!elem) {
+ g_warning ("%s: Cannot find menu with id '%s'", G_STRFUNC, id);
+ return;
+ }
+
+ if (e_ui_element_get_kind (elem) != E_UI_ELEMENT_KIND_MENU) {
+ g_warning ("%s: Item with ID '%s' is not a menu, it's '%s' instead", G_STRFUNC,
+ id, e_enum_to_string (E_TYPE_UI_ELEMENT_KIND, e_ui_element_get_kind (elem)));
+ return;
+ }
+
+ eum_traverse_menu (self, ui_menu, elem, NULL, e_ui_element_menu_get_is_popup (elem), §ion);
+
+ if (section && g_menu_model_get_n_items (G_MENU_MODEL (section)) > 0)
+ e_ui_menu_append_section (ui_menu, G_MENU_MODEL (section));
+
+ g_clear_object (§ion);
+}
+
+static gboolean
+e_ui_manager_transform_without_underscores_cb (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ const gchar *text = g_value_get_string (from_value);
+
+ if (!text || !strchr (text, '_'))
+ g_value_set_string (to_value, text);
+ else
+ g_value_take_string (to_value, e_str_without_underscores (text));
+
+ return TRUE;
+}
+
+static void
+e_ui_manager_create_named_binding (EUIManager *self,
+ gboolean strip_underscores,
+ const gchar *binding_name,
+ EUIAction *action,
+ const gchar *action_prop_name,
+ gpointer item,
+ const gchar *item_prop_name)
+{
+ GWeakRef *weakref;
+ GBinding *binding;
+
+ weakref = g_object_get_data (G_OBJECT (item), binding_name);
+ binding = weakref ? g_weak_ref_get (weakref) : NULL;
+ if (binding) {
+ g_binding_unbind (binding);
+ g_object_unref (binding);
+ }
+
+ if (strip_underscores) {
+ binding = e_binding_bind_property_full (action, action_prop_name,
+ item, item_prop_name,
+ G_BINDING_SYNC_CREATE,
+ e_ui_manager_transform_without_underscores_cb,
+ NULL, NULL, NULL);
+ } else {
+ binding = e_binding_bind_property (action, action_prop_name,
+ item, item_prop_name,
+ G_BINDING_SYNC_CREATE);
+ }
+
+ g_object_set_data_full (G_OBJECT (item), binding_name, e_weak_ref_new (binding), (GDestroyNotify) e_weak_ref_free);
+}
+
+static void
+e_ui_manager_synchro_menu_item_attribute (EUIAction *action,
+ const gchar *prop_name,
+ GMenuItem *item)
+{
+ const gchar *value;
+
+ if (g_strcmp0 (prop_name, "label") == 0) {
+ value = e_ui_action_get_label (action);
+ g_menu_item_set_label (item, value ? value : "");
+ } else if (g_strcmp0 (prop_name, "accel") == 0) {
+ value = e_ui_action_get_accel (action);
+ g_menu_item_set_attribute (item, "accel", value ? "s" : NULL, value);
+ } else {
+ g_warning ("%s: Unhandled property '%s'", G_STRFUNC, prop_name);
+ }
+}
+
+static void
+e_ui_manager_synchro_menu_item_attribute_cb (EUIAction *action,
+ GParamSpec *param,
+ GMenuItem *item)
+{
+ g_return_if_fail (param != NULL);
+
+ e_ui_manager_synchro_menu_item_attribute (action, param->name, item);
+}
+
+/**
+ * e_ui_manager_update_item_from_action:
+ * @self: an #EUIManager
+ * @item: an item object, its type varies based on the place of use
+ * @action: an #EUIAction to update the @item with
+ *
+ * Updates properties of the @item with the values from the @action.
+ * The function can handle only #GMenuItem, #GtkToolButton,
+ * #GtkButton and #EHeaderBarButton descendants.
+ *
+ * Passing other than supported types into the function is
+ * considered a programming error.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_manager_update_item_from_action (EUIManager *self,
+ gpointer item,
+ EUIAction *action)
+{
+ GAction *gaction;
+ GVariant *target;
+ const GVariantType *param_type;
+ const gchar *value;
+ gchar *action_name_full;
+
+ g_return_if_fail (E_IS_UI_MANAGER (self));
+ g_return_if_fail (E_IS_UI_ACTION (action));
+
+ if (!item)
+ return;
+
+ gaction = G_ACTION (action);
+ value = g_action_get_name (gaction);
+ param_type = g_action_get_parameter_type (gaction);
+ target = e_ui_action_ref_target (action);
+
+ action_name_full = g_strconcat (e_ui_action_get_map_name (action), ".", value, NULL);
+
+ if (G_IS_MENU_ITEM (item)) {
+ g_menu_item_set_action_and_target_value (item, action_name_full, param_type && target ? target : NULL);
+
+ g_signal_handlers_disconnect_by_func (action, G_CALLBACK (e_ui_manager_synchro_menu_item_attribute_cb), item);
+
+ g_signal_connect_object (action, "notify::label",
+ G_CALLBACK (e_ui_manager_synchro_menu_item_attribute_cb), item, 0);
+ g_signal_connect_object (action, "notify::accel",
+ G_CALLBACK (e_ui_manager_synchro_menu_item_attribute_cb), item, 0);
+
+ e_ui_manager_synchro_menu_item_attribute (action, "label", item);
+ e_ui_manager_synchro_menu_item_attribute (action, "accel", item);
+
+ value = e_ui_action_get_icon_name (action);
+ if (value) {
+ if (g_str_has_prefix (value, "gicon::")) {
+ GIcon *icon;
+
+ icon = e_ui_manager_get_gicon (self, value + 7);
+ if (icon)
+ g_menu_item_set_icon (item, icon);
+ } else {
+ g_menu_item_set_attribute (item, "icon", "s", value);
+ }
+ }
+ } else if (GTK_IS_TOOL_BUTTON (item)) {
+ gtk_actionable_set_action_name (GTK_ACTIONABLE (item), action_name_full);
+ if (param_type && target)
+ gtk_actionable_set_action_target_value (GTK_ACTIONABLE (item), target);
+
+ gtk_tool_button_set_use_underline (item, TRUE);
+
+ value = e_ui_action_get_icon_name (action);
+ if (value) {
+ if (g_str_has_prefix (value, "gicon::")) {
+ GIcon *icon;
+
+ icon = e_ui_manager_get_gicon (self, value + 7);
+ if (icon) {
+ GtkWidget *image;
+
+ image = gtk_image_new_from_gicon (icon, gtk_tool_item_get_icon_size (item));
+ gtk_widget_set_visible (image, TRUE);
+ gtk_tool_button_set_icon_widget (item, image);
+ }
+ } else {
+ gtk_tool_button_set_icon_name (item, value);
+ }
+ }
+
+ e_ui_manager_create_named_binding (self, FALSE, "EUIManager::binding:label",
+ action, "label",
+ item, "label");
+
+ e_ui_manager_create_named_binding (self, FALSE, "EUIManager::binding:tooltip",
+ action, "tooltip",
+ item, "tooltip-text");
+ } else if (GTK_IS_BUTTON (item)) {
+ gboolean headerbar_parent;
+
+ gtk_actionable_set_action_name (GTK_ACTIONABLE (item), action_name_full);
+ if (param_type && target)
+ gtk_actionable_set_action_target_value (GTK_ACTIONABLE (item), target);
+
+ headerbar_parent = E_IS_HEADER_BAR_BUTTON (gtk_widget_get_parent (item));
+
+ /* mnemonics on the header bar button can steal menu mnemonics */
+ gtk_button_set_use_underline (item, !headerbar_parent);
+
+ value = e_ui_action_get_icon_name (action);
+ if (value) {
+ GtkWidget *image = NULL;
+
+ if (g_str_has_prefix (value, "gicon::")) {
+ GIcon *icon;
+
+ icon = e_ui_manager_get_gicon (self, value + 7);
+ if (icon)
+ image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_BUTTON);
+ } else {
+ image = gtk_image_new_from_icon_name (value, GTK_ICON_SIZE_BUTTON);
+ }
+
+ if (image) {
+ gtk_button_set_image (item, image);
+ gtk_widget_show (image);
+ } else {
+ gtk_button_set_image (item, NULL);
+ }
+ } else {
+ gtk_button_set_image (item, NULL);
+ }
+
+ e_ui_manager_create_named_binding (self, headerbar_parent, "EUIManager::binding:label",
+ action, "label",
+ item, "label");
+
+ e_ui_manager_create_named_binding (self, FALSE, "EUIManager::binding:tooltip",
+ action, "tooltip",
+ item, "tooltip-text");
+ } else if (E_IS_HEADER_BAR_BUTTON (item)) {
+ } else {
+ g_warning ("%s: Do not know how to update item '%s'", G_STRFUNC,
+ item ? G_OBJECT_TYPE_NAME (item) : "null");
+ }
+
+ if (GTK_IS_WIDGET (item)) {
+ /* This is called for both labeled and icon-only buttons of the EHeaderBarButton
+ widget, but those two take care of the correct visibility on their own,
+ thus do not influence it here. */
+ if (!eum_parent_is_header_bar_button (gtk_widget_get_parent (item))) {
+ e_ui_manager_create_named_binding (self, FALSE, "EUIManager::binding:visible",
+ action, "is-visible",
+ item, "visible");
+ }
+
+ e_ui_manager_create_named_binding (self, FALSE, "EUIManager::binding:enabled",
+ action, "enabled",
+ item, "sensitive");
+ }
+
+ g_clear_pointer (&target, g_variant_unref);
+ g_clear_pointer (&action_name_full, g_free);
+}
+
+
+/**
+ * e_ui_manager_create_item_from_menu_model:
+ * @self: an #EUIManager
+ * @elem: (nullable): corresponding #EUIElement, or %NULL
+ * @action: an #EUIAction
+ * @for_kind: for which part of the UI the item should be created; it influences the type of the returned item
+ * @menu_model: (transfer none): a #GMenuModel to use for the created item
+ *
+ * Creates a new UI item suitable for the @for_kind, containing the @menu_model.
+ * For %E_UI_ELEMENT_KIND_MENU returns a submenu #GMenuItem;
+ * for %E_UI_ELEMENT_KIND_TOOLBAR returns a #GtkMenuToolButton and
+ * for %E_UI_ELEMENT_KIND_HEADERBAR returns an #EHeaderBarButton.
+ *
+ * This might be usually called from #EUIManager 's "create-item" callback.
+ *
+ * Returns: (transfer full): a new UI item
+ *
+ * Since: 3.56
+ **/
+GObject *
+e_ui_manager_create_item_from_menu_model (EUIManager *self,
+ EUIElement *elem,
+ EUIAction *action,
+ EUIElementKind for_kind,
+ GMenuModel *menu_model)
+{
+ GObject *item = NULL;
+
+ g_return_val_if_fail (E_IS_UI_MANAGER (self), NULL);
+ g_return_val_if_fail (E_IS_UI_ACTION (action), NULL);
+ g_return_val_if_fail (G_IS_MENU_MODEL (menu_model), NULL);
+
+ if (for_kind == E_UI_ELEMENT_KIND_MENU) {
+ item = G_OBJECT (g_menu_item_new_submenu (e_ui_action_get_label (action), menu_model));
+ } else if (for_kind == E_UI_ELEMENT_KIND_TOOLBAR) {
+ GtkToolItem *tool_item;
+ GtkWidget *menu;
+
+ menu = gtk_menu_new_from_model (menu_model);
+ tool_item = gtk_menu_tool_button_new (NULL, e_ui_action_get_label (action));
+ gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (tool_item), menu);
+
+ if (elem) {
+ e_ui_manager_add_css_classes (self, GTK_WIDGET (tool_item), e_ui_element_item_get_css_classes (elem));
+ gtk_tool_item_set_is_important (tool_item, e_ui_element_item_get_important (elem));
+ } else {
+ gtk_tool_item_set_is_important (tool_item, TRUE);
+ }
+
+ e_ui_manager_update_item_from_action (self, tool_item, action);
+ e_ui_action_util_assign_to_widget (action, GTK_WIDGET (tool_item));
+
+ item = G_OBJECT (tool_item);
+ } else if (for_kind == E_UI_ELEMENT_KIND_HEADERBAR) {
+ GtkWidget *widget, *menu;
+
+ menu = gtk_menu_new_from_model (menu_model);
+
+ widget = e_header_bar_button_new (e_ui_action_get_label (action), action, self);
+
+ e_header_bar_button_take_menu (E_HEADER_BAR_BUTTON (widget), menu);
+
+ e_binding_bind_property (
+ action, "sensitive",
+ widget, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ e_binding_bind_property (
+ action, "visible",
+ widget, "visible",
+ G_BINDING_SYNC_CREATE);
+
+ item = G_OBJECT (widget);
+ } else {
+ g_warning ("%s: Unhandled element kind '%d' for action '%s'", G_STRFUNC, (gint) for_kind, g_action_get_name (G_ACTION (action)));
+ }
+
+ return item;
+}
diff --git a/src/e-util/e-ui-manager.h b/src/e-util/e-ui-manager.h
new file mode 100644
index 0000000000..0c1c489224
--- /dev/null
+++ b/src/e-util/e-ui-manager.h
@@ -0,0 +1,97 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * SPDX-FileCopyrightText: (C) 2024 Red Hat (www.redhat.com>
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only should be included directly."
+#endif
+
+#ifndef E_UI_MANAGER_H
+#define E_UI_MANAGER_H
+
+#include
+
+#include
+#include
+#include
+#include
+
+G_BEGIN_DECLS
+
+struct _EUIMenu;
+
+#define E_TYPE_UI_MANAGER e_ui_manager_get_type ()
+
+G_DECLARE_FINAL_TYPE (EUIManager, e_ui_manager, E, UI_MANAGER, GObject)
+
+EUIManager * e_ui_manager_new (void);
+EUIParser * e_ui_manager_get_parser (EUIManager *self);
+GtkAccelGroup * e_ui_manager_get_accel_group (EUIManager *self);
+void e_ui_manager_freeze (EUIManager *self);
+void e_ui_manager_thaw (EUIManager *self);
+gboolean e_ui_manager_is_frozen (EUIManager *self);
+void e_ui_manager_add_actions (EUIManager *self,
+ const gchar *group_name,
+ const gchar *translation_domain,
+ const EUIActionEntry *entries,
+ gint n_entries,
+ gpointer user_data);
+void e_ui_manager_add_actions_enum (EUIManager *self,
+ const gchar *group_name,
+ const gchar *translation_domain,
+ const EUIActionEnumEntry *entries,
+ gint n_entries,
+ gpointer user_data);
+void e_ui_manager_add_action (EUIManager *self,
+ const gchar *group_name,
+ EUIAction *action,
+ EUIActionFunc activate,
+ EUIActionFunc change_state,
+ gpointer user_data);
+void e_ui_manager_add_actions_with_eui_data
+ (EUIManager *self,
+ const gchar *group_name,
+ const gchar *translation_domain,
+ const EUIActionEntry *entries,
+ gint n_entries,
+ gpointer user_data,
+ const gchar *eui);
+void e_ui_manager_add_action_groups_to_widget
+ (EUIManager *self,
+ GtkWidget *widget);
+void e_ui_manager_set_action_groups_widget
+ (EUIManager *self,
+ GtkWidget *widget);
+GtkWidget * e_ui_manager_ref_action_groups_widget
+ (EUIManager *self);
+gboolean e_ui_manager_has_action_group (EUIManager *self,
+ const gchar *name);
+EUIActionGroup *e_ui_manager_get_action_group (EUIManager *self,
+ const gchar *name);
+void e_ui_manager_add_action_group (EUIManager *self,
+ EUIActionGroup *action_group);
+EUIAction * e_ui_manager_get_action (EUIManager *self,
+ const gchar *name);
+GIcon * e_ui_manager_get_gicon (EUIManager *self,
+ const gchar *name);
+GObject * e_ui_manager_create_item (EUIManager *self,
+ const gchar *id);
+void e_ui_manager_fill_menu (EUIManager *self,
+ const gchar *id,
+ struct _EUIMenu *ui_menu);
+void e_ui_manager_update_item_from_action
+ (EUIManager *self,
+ gpointer item,
+ EUIAction *action);
+GObject * e_ui_manager_create_item_from_menu_model
+ (EUIManager *self,
+ EUIElement *elem,
+ EUIAction *action,
+ EUIElementKind for_kind,
+ GMenuModel *menu_model);
+
+G_END_DECLS
+
+#endif /* E_UI_MANAGER_H */
diff --git a/src/e-util/e-ui-menu.c b/src/e-util/e-ui-menu.c
new file mode 100644
index 0000000000..4ee3070e99
--- /dev/null
+++ b/src/e-util/e-ui-menu.c
@@ -0,0 +1,517 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * SPDX-FileCopyrightText: (C) 2024 Red Hat (www.redhat.com>
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "evolution-config.h"
+
+#include
+
+#include "e-ui-action.h"
+#include "e-ui-manager.h"
+
+#include "e-ui-menu.h"
+
+/**
+ * SECTION: e-ui-menu
+ * @include: e-util/e-util.h
+ * @short_description: a UI menu
+ *
+ * #EUIMenu is a #GMenuModel descendant, which takes care of an #EUIAction
+ * visibility and regenerates its content when any of the actions hides/shows
+ * itself or when the associated #EUIManager changes.
+ *
+ * The object is not thread-safe, it's meant to be used only from
+ * the main/GUI thread.
+ *
+ * Since: 3.56
+ **/
+
+struct _EUIMenu {
+ GMenuModel parent;
+
+ GHashTable *tracked_actions; /* EUIAction * ~> NULL */
+ GMenu *real_menu;
+ EUIManager *manager;
+ gchar *id;
+
+ guint frozen;
+ gboolean changed_while_frozen;
+};
+
+G_DEFINE_TYPE (EUIMenu, e_ui_menu, G_TYPE_MENU_MODEL)
+
+enum {
+ PROP_0,
+ PROP_MANAGER,
+ PROP_ID,
+ N_PROPS
+};
+
+static GParamSpec *properties[N_PROPS] = { NULL, };
+
+static void
+e_ui_menu_thaw_internal (EUIMenu *self)
+{
+ g_return_if_fail (E_IS_UI_MENU (self));
+ g_return_if_fail (self->frozen > 0);
+
+ self->frozen--;
+
+ if (!self->frozen && self->changed_while_frozen) {
+ self->changed_while_frozen = FALSE;
+
+ e_ui_menu_rebuild (self);
+ }
+}
+
+static void
+e_ui_menu_manager_freeze_cb (EUIManager *manager,
+ gpointer user_data)
+{
+ EUIMenu *self = user_data;
+
+ e_ui_menu_freeze (self);
+}
+
+static void
+e_ui_menu_manager_thaw_cb (EUIManager *manager,
+ gboolean changed_while_frozen,
+ gpointer user_data)
+{
+ EUIMenu *self = user_data;
+
+ self->changed_while_frozen = self->changed_while_frozen || changed_while_frozen;
+
+ e_ui_menu_thaw_internal (self);
+}
+
+static void
+e_ui_menu_items_changed_cb (GMenuModel *parent,
+ gint position,
+ gint removed,
+ gint added,
+ gpointer user_data)
+{
+ EUIMenu *self = user_data;
+
+ g_menu_model_items_changed (G_MENU_MODEL (self), position, removed, added);
+}
+
+static gboolean
+e_ui_menu_is_mutable (GMenuModel *self)
+{
+ return TRUE;
+}
+
+static gint
+e_ui_menu_get_n_items (GMenuModel *model)
+{
+ EUIMenu *self = E_UI_MENU (model);
+
+ return g_menu_model_get_n_items (G_MENU_MODEL (self->real_menu));
+}
+
+static void
+e_ui_menu_get_item_attributes (GMenuModel *model,
+ gint position,
+ GHashTable **table)
+{
+ EUIMenu *self = E_UI_MENU (model);
+ GMenuModelClass *klass;
+
+ klass = G_MENU_MODEL_GET_CLASS (self->real_menu);
+ g_return_if_fail (klass != NULL);
+ g_return_if_fail (klass->get_item_attributes != NULL);
+
+ klass->get_item_attributes (G_MENU_MODEL (self->real_menu), position, table);
+}
+
+static void
+e_ui_menu_get_item_links (GMenuModel *model,
+ gint position,
+ GHashTable **table)
+{
+ EUIMenu *self = E_UI_MENU (model);
+ GMenuModelClass *klass;
+
+ klass = G_MENU_MODEL_GET_CLASS (self->real_menu);
+ g_return_if_fail (klass != NULL);
+ g_return_if_fail (klass->get_item_links != NULL);
+
+ klass->get_item_links (G_MENU_MODEL (self->real_menu), position, table);
+}
+
+static void
+e_ui_menu_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EUIMenu *self = E_UI_MENU (object);
+
+ switch (prop_id) {
+ case PROP_MANAGER:
+ g_clear_object (&self->manager);
+ self->manager = g_value_dup_object (value);
+ break;
+ case PROP_ID:
+ g_free (self->id);
+ self->id = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+e_ui_menu_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EUIMenu *self = E_UI_MENU (object);
+
+ switch (prop_id) {
+ case PROP_MANAGER:
+ g_value_set_object (value, e_ui_menu_get_manager (self));
+ break;
+ case PROP_ID:
+ g_value_set_string (value, e_ui_menu_get_id (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+e_ui_menu_constructed (GObject *object)
+{
+ EUIMenu *self = E_UI_MENU (object);
+
+ G_OBJECT_CLASS (e_ui_menu_parent_class)->constructed (object);
+
+ g_return_if_fail (self->manager != NULL);
+ g_return_if_fail (self->id != NULL);
+
+ e_ui_menu_rebuild (self);
+
+ g_signal_connect_object (self->manager, "changed",
+ G_CALLBACK (e_ui_menu_rebuild), self, G_CONNECT_SWAPPED);
+ g_signal_connect_object (self->manager, "freeze",
+ G_CALLBACK (e_ui_menu_manager_freeze_cb), self, 0);
+ g_signal_connect_object (self->manager, "thaw",
+ G_CALLBACK (e_ui_menu_manager_thaw_cb), self, 0);
+}
+
+static void
+e_ui_menu_finalize (GObject *object)
+{
+ EUIMenu *self = E_UI_MENU (object);
+
+ e_ui_menu_remove_all (self);
+
+ g_clear_pointer (&self->tracked_actions, g_hash_table_unref);
+ g_clear_pointer (&self->id, g_free);
+ g_clear_object (&self->real_menu);
+ g_clear_object (&self->manager);
+
+ G_OBJECT_CLASS (e_ui_menu_parent_class)->finalize (object);
+}
+
+static void
+e_ui_menu_class_init (EUIMenuClass *klass)
+{
+ GObjectClass *object_class;
+ GMenuModelClass *model_class;
+
+ model_class = G_MENU_MODEL_CLASS (klass);
+ model_class->is_mutable = e_ui_menu_is_mutable;
+ model_class->get_n_items = e_ui_menu_get_n_items;
+ model_class->get_item_attributes = e_ui_menu_get_item_attributes;
+ model_class->get_item_links = e_ui_menu_get_item_links;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->set_property = e_ui_menu_set_property;
+ object_class->get_property = e_ui_menu_get_property;
+ object_class->constructed = e_ui_menu_constructed;
+ object_class->finalize = e_ui_menu_finalize;
+
+ /**
+ * EUIMenu:manager:
+ *
+ * An #EUIManager associated with the menu.
+ *
+ * Since: 3.56
+ **/
+ properties[PROP_MANAGER] = g_param_spec_object ("manager", NULL, NULL,
+ E_TYPE_UI_MANAGER,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * EUIMenu:id:
+ *
+ * Identifier of the menu to be read from the #EUIManager.
+ *
+ * Since: 3.56
+ **/
+ properties[PROP_ID] = g_param_spec_string ("id", NULL, NULL, NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
+}
+
+static void
+e_ui_menu_init (EUIMenu *self)
+{
+ self->tracked_actions = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
+ self->real_menu = g_menu_new ();
+
+ g_signal_connect_object (self->real_menu, "items-changed",
+ G_CALLBACK (e_ui_menu_items_changed_cb), self, 0);
+}
+
+/**
+ * e_ui_menu_new:
+ * @manager: an *EUIManager
+ * @id: a menu identifier to use the created #EUIMenu with
+ *
+ * Creates a new #EUIMenu, which will use the @manager to get
+ * its content from under identifier @id.
+ *
+ * Returns: (transfer full): a new #EUIMenu
+ *
+ * Since: 3.56
+ **/
+EUIMenu *
+e_ui_menu_new (EUIManager *manager,
+ const gchar *id)
+{
+ return g_object_new (E_TYPE_UI_MENU,
+ "manager", manager,
+ "id", id,
+ NULL);
+}
+
+/**
+ * e_ui_menu_get_manager:
+ * @self: an #EUIMenu
+ *
+ * Gets an associated #EUIManager.
+ *
+ * Returns: (transfer none): an associated #EUIManager
+ *
+ * Since: 3.56
+ **/
+EUIManager *
+e_ui_menu_get_manager (EUIMenu *self)
+{
+ g_return_val_if_fail (E_IS_UI_MENU (self), NULL);
+
+ return self->manager;
+}
+
+/**
+ * e_ui_menu_get_id:
+ * @self: an #EUIMenu
+ *
+ * Gets an identifier of the menu to populate the @self with.
+ *
+ * Returns: a menu identifier
+ *
+ * Since: 3.56
+ **/
+const gchar *
+e_ui_menu_get_id (EUIMenu *self)
+{
+ g_return_val_if_fail (E_IS_UI_MENU (self), NULL);
+
+ return self->id;
+}
+
+/**
+ * e_ui_menu_append_item:
+ * @self: an #EUIMenu
+ * @action: (nullable): an #EUIAction, or %NULL
+ * @item: a #GMenuItem to append
+ *
+ * Appends a #GMenuItem descendant @item into the menu, which can
+ * be related to the @action. When the @action is not %NULL, its
+ * state is tracked and the menu is regenerated whenever the state
+ * changed (and when needed).
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_menu_append_item (EUIMenu *self,
+ EUIAction *action,
+ GMenuItem *item)
+{
+ g_return_if_fail (E_IS_UI_MENU (self));
+ g_return_if_fail (G_IS_MENU_ITEM (item));
+
+ if (action)
+ e_ui_menu_track_action (self, action);
+
+ g_menu_append_item (self->real_menu, item);
+}
+
+/**
+ * e_ui_menu_append_section:
+ * @self: an #EUIMenu
+ * @section: a #GMenuModel
+ *
+ * Appends a #GMenuModel as a new section in the @self.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_menu_append_section (EUIMenu *self,
+ GMenuModel *section)
+{
+ g_return_if_fail (E_IS_UI_MENU (self));
+ g_return_if_fail (G_IS_MENU_MODEL (section));
+
+ g_menu_append_section (self->real_menu, NULL, section);
+}
+
+/**
+ * e_ui_menu_track_action:
+ * @self: an #EUIMenu
+ * @action: (not nullable): an #EUIAction
+ *
+ * Tracks a state change of the @action and regenerates the menu
+ * content when needed.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_menu_track_action (EUIMenu *self,
+ EUIAction *action)
+{
+ g_return_if_fail (E_IS_UI_MENU (self));
+ g_return_if_fail (E_IS_UI_ACTION (action));
+
+ if (!g_hash_table_contains (self->tracked_actions, action)) {
+ g_signal_connect_swapped (action, "notify::is-visible", G_CALLBACK (e_ui_menu_rebuild), self);
+ g_signal_connect_swapped (action, "changed", G_CALLBACK (e_ui_menu_rebuild), self);
+ g_hash_table_add (self->tracked_actions, g_object_ref (action));
+ }
+}
+
+/**
+ * e_ui_menu_freeze:
+ * @self: an #EUIMenu
+ *
+ * Freezes rebuild of the menu content. Useful when filling the content.
+ * The function can be called multiple times, only each call needs
+ * a pair call of the e_ui_menu_thaw() to revert the effect of this function.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_menu_freeze (EUIMenu *self)
+{
+ g_return_if_fail (E_IS_UI_MENU (self));
+ g_return_if_fail (self->frozen + 1 > self->frozen);
+
+ self->frozen++;
+}
+
+/**
+ * e_ui_menu_thaw:
+ * @self: an #EUIMenu
+ *
+ * Pair function for the e_ui_menu_freeze(). It's a programming
+ * error to thaw a menu, which is not frozen.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_menu_thaw (EUIMenu *self)
+{
+ g_return_if_fail (E_IS_UI_MENU (self));
+ g_return_if_fail (self->frozen > 0);
+
+ e_ui_menu_thaw_internal (self);
+}
+
+/**
+ * e_ui_menu_is_frozen:
+ * @self: an #EUIMenu
+ *
+ * Gets whether the @self is frozen for rebuild. It can be frozen with
+ * the e_ui_menu_freeze() and unfrozen with the e_ui_menu_thaw().
+ *
+ * Returns: whether rebuild of the @self is frozen
+ *
+ * Since: 3.56
+ **/
+gboolean
+e_ui_menu_is_frozen (EUIMenu *self)
+{
+ g_return_val_if_fail (E_IS_UI_MENU (self), FALSE);
+
+ return self->frozen > 0;
+}
+
+/**
+ * e_ui_menu_rebuild:
+ * @self: an #EUIMenu
+ *
+ * Rebuilds the @self content. If the rebuild is frozen (see e_ui_menu_freeze()),
+ * the rebuild is postponed until the rebuild is allowed again.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_menu_rebuild (EUIMenu *self)
+{
+ g_return_if_fail (E_IS_UI_MENU (self));
+
+ if (self->frozen) {
+ self->changed_while_frozen = TRUE;
+ return;
+ }
+
+ e_ui_menu_remove_all (self);
+ e_ui_manager_fill_menu (self->manager, self->id, self);
+}
+
+/**
+ * e_ui_menu_remove_all:
+ * @self: an #EUIMenu
+ *
+ * Cleans up content of the @self, including tracked actions
+ * and all the menu items.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_menu_remove_all (EUIMenu *self)
+{
+ GHashTableIter iter;
+ gpointer key;
+
+ g_return_if_fail (E_IS_UI_MENU (self));
+
+ g_menu_remove_all (self->real_menu);
+
+ g_hash_table_iter_init (&iter, self->tracked_actions);
+ while (g_hash_table_iter_next (&iter, &key, NULL)) {
+ EUIAction *action = key;
+
+ g_signal_handlers_disconnect_by_func (action, G_CALLBACK (e_ui_menu_rebuild), self);
+ }
+
+ g_hash_table_remove_all (self->tracked_actions);
+}
diff --git a/src/e-util/e-ui-menu.h b/src/e-util/e-ui-menu.h
new file mode 100644
index 0000000000..0e020fed6c
--- /dev/null
+++ b/src/e-util/e-ui-menu.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * SPDX-FileCopyrightText: (C) 2024 Red Hat (www.redhat.com>
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only should be included directly."
+#endif
+
+#ifndef E_UI_MENU_H
+#define E_UI_MENU_H
+
+#include
+
+#include
+#include
+
+G_BEGIN_DECLS
+
+#define E_TYPE_UI_MENU e_ui_menu_get_type ()
+
+G_DECLARE_FINAL_TYPE (EUIMenu, e_ui_menu, E, UI_MENU, GMenuModel)
+
+EUIMenu * e_ui_menu_new (EUIManager *manager,
+ const gchar *id);
+EUIManager * e_ui_menu_get_manager (EUIMenu *self);
+const gchar * e_ui_menu_get_id (EUIMenu *self);
+void e_ui_menu_append_item (EUIMenu *self,
+ EUIAction *action,
+ GMenuItem *item);
+void e_ui_menu_append_section (EUIMenu *self,
+ GMenuModel *section);
+void e_ui_menu_track_action (EUIMenu *self,
+ EUIAction *action);
+void e_ui_menu_freeze (EUIMenu *self);
+void e_ui_menu_thaw (EUIMenu *self);
+gboolean e_ui_menu_is_frozen (EUIMenu *self);
+void e_ui_menu_rebuild (EUIMenu *self);
+void e_ui_menu_remove_all (EUIMenu *self);
+
+G_END_DECLS
+
+#endif /* E_UI_MENU_H */
diff --git a/src/e-util/e-ui-parser.c b/src/e-util/e-ui-parser.c
new file mode 100644
index 0000000000..275aecbc2c
--- /dev/null
+++ b/src/e-util/e-ui-parser.c
@@ -0,0 +1,1469 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * SPDX-FileCopyrightText: (C) 2024 Red Hat (www.redhat.com>
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "evolution-config.h"
+
+#include
+#include
+
+#include "e-misc-utils.h"
+#include "e-util-enums.h"
+
+#include "e-ui-parser.h"
+
+/**
+ * SECTION: e-ui-parser
+ * @include: e-util/e-util.h
+ * @short_description: a UI definition parser
+ *
+ * #EUIParser parses UI definitions for menus, headerbar and toolbar from an XML data.
+ * It's associated with an #EUIManager.
+ *
+ * The object is not thread-safe, it's meant to be used only from
+ * the main/GUI thread.
+ *
+ * Since: 3.56
+ **/
+
+/*
+ Example:
+
+
+
+
+ "
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+*/
+
+static const gchar *
+e_ui_element_kind_to_string (EUIElementKind kind)
+{
+ switch (kind) {
+ case E_UI_ELEMENT_KIND_UNKNOWN:
+ return "unknown";
+ case E_UI_ELEMENT_KIND_ROOT:
+ return "eui";
+ case E_UI_ELEMENT_KIND_HEADERBAR:
+ return "headerbar";
+ case E_UI_ELEMENT_KIND_TOOLBAR:
+ return "toolbar";
+ case E_UI_ELEMENT_KIND_MENU:
+ return "menu";
+ case E_UI_ELEMENT_KIND_SUBMENU:
+ return "submenu";
+ case E_UI_ELEMENT_KIND_PLACEHOLDER:
+ return "placeholder";
+ case E_UI_ELEMENT_KIND_SEPARATOR:
+ return "separator";
+ case E_UI_ELEMENT_KIND_START:
+ return "start";
+ case E_UI_ELEMENT_KIND_END:
+ return "end";
+ case E_UI_ELEMENT_KIND_ITEM:
+ return "item";
+ }
+
+ return "???";
+}
+
+struct _EUIElement {
+ EUIElementKind kind;
+ gchar *id;
+ GPtrArray *children;
+ union {
+ /* menu properties */
+ struct _menu {
+ gboolean is_popup;
+ } menu;
+
+ /* submenu properties */
+ struct _submenu {
+ gchar *action;
+ } submenu;
+
+ /* headerbar properties */
+ struct _headerbar {
+ gboolean use_gtk_type;
+ } headerbar;
+
+ /* toolbar properties */
+ struct _toolbar {
+ gboolean primary;
+ } toolbar;
+
+ /* item properties */
+ struct _item {
+ guint label_priority;
+ gint order;
+ gint icon_only;
+ gint text_only;
+ gboolean important;
+ gchar *css_classes;
+ gchar *action;
+ gchar *group;
+ } item;
+ } data;
+};
+
+static EUIElement *
+e_ui_element_new (EUIElementKind kind,
+ const gchar *id)
+{
+ EUIElement *self;
+
+ self = g_new0 (EUIElement, 1);
+ self->kind = kind;
+ self->id = g_strdup (id);
+
+ if (kind == E_UI_ELEMENT_KIND_ITEM) {
+ self->data.item.label_priority = G_MAXUINT32;
+ self->data.item.order = 0;
+ self->data.item.icon_only = G_MAXINT32;
+ self->data.item.text_only = G_MAXINT32;
+ self->data.item.important = FALSE;
+ }
+
+ return self;
+}
+
+static void
+e_ui_element_free (EUIElement *self)
+{
+ if (!self)
+ return;
+
+ g_clear_pointer (&self->id, g_free);
+ g_clear_pointer (&self->children, g_ptr_array_unref);
+
+ if (self->kind == E_UI_ELEMENT_KIND_SUBMENU) {
+ g_clear_pointer (&self->data.submenu.action, g_free);
+ } else if (self->kind == E_UI_ELEMENT_KIND_ITEM) {
+ g_clear_pointer (&self->data.item.css_classes, g_free);
+ g_clear_pointer (&self->data.item.action, g_free);
+ g_clear_pointer (&self->data.item.group, g_free);
+ }
+
+ g_free (self);
+}
+
+static void
+e_ui_element_add_child (EUIElement *parent,
+ EUIElement *child /* (transfer full) */)
+{
+ g_return_if_fail (parent != NULL);
+ g_return_if_fail (child != NULL);
+
+ if (!parent->children)
+ parent->children = g_ptr_array_new_with_free_func ((GDestroyNotify) e_ui_element_free);
+
+ g_ptr_array_add (parent->children, child);
+}
+
+/**
+ * e_ui_element_get_kind:
+ * @self: an #EUIElement
+ *
+ * Gets the kind of the @self.
+ *
+ * Returns: an #EUIElementKind of the @self
+ *
+ * Since: 3.56
+ **/
+EUIElementKind
+e_ui_element_get_kind (const EUIElement *self)
+{
+ g_return_val_if_fail (self != NULL, E_UI_ELEMENT_KIND_UNKNOWN);
+ return self->kind;
+}
+
+/**
+ * e_ui_element_get_id:
+ * @self: an #EUIElement
+ *
+ * Gets the identifier of the @self.
+ *
+ * Returns: (nullable): an identifier of the @self
+ *
+ * Since: 3.56
+ **/
+const gchar *
+e_ui_element_get_id (const EUIElement *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ return self->id;
+}
+
+/**
+ * e_ui_element_get_n_children:
+ * @self: an #EUIElement
+ *
+ * Gets how many children the element has.
+ *
+ * Returns: count of children of the @self
+ *
+ * Since: 3.56
+ **/
+guint
+e_ui_element_get_n_children (const EUIElement *self)
+{
+ g_return_val_if_fail (self != NULL, 0);
+ return self->children ? self->children->len : 0;
+}
+
+/**
+ * e_ui_element_get_child:
+ * @self: an #EUIElement
+ * @index: an index of the child to get
+ *
+ * Gets a child at @index, which should be less
+ * than e_ui_element_get_n_children().
+ *
+ * Returns: (transfer none) (nullable): a child element at index @index,
+ * or %NULL, when @index is out of bounds
+ *
+ * Since: 3.56
+ **/
+EUIElement *
+e_ui_element_get_child (EUIElement *self,
+ guint index)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+
+ if (!self->children || index >= self->children->len)
+ return NULL;
+
+ return g_ptr_array_index (self->children, index);
+}
+
+/**
+ * e_ui_element_get_child_by_id:
+ * @self: an #EUIElement
+ * @id: an identified of the child to get
+ *
+ * Gets a child by its identifier. It searches the direct
+ * children of the @self only.
+ *
+ * Returns: (transfer none) (nullable): a child element with identifier @id,
+ * or %NULL, when not found
+ *
+ * Since: 3.56
+ **/
+EUIElement *
+e_ui_element_get_child_by_id (EUIElement *self,
+ const gchar *id)
+{
+ guint ii;
+
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (id != NULL, NULL);
+
+ for (ii = 0; self->children && ii < self->children->len; ii++) {
+ EUIElement *adept = g_ptr_array_index (self->children, ii);
+
+ if (g_strcmp0 (adept->id, id) == 0)
+ return adept;
+ }
+
+ return NULL;
+}
+
+static void
+e_ui_element_export (const EUIElement *self,
+ GString *str,
+ gint indent)
+{
+ if (!self)
+ return;
+
+ if (indent >= 0)
+ g_string_append_printf (str, "%*s", indent * 2, "");
+
+ g_string_append_printf (str, "<%s", e_ui_element_kind_to_string (self->kind));
+
+ if (self->id)
+ e_util_markup_append_escaped (str, " id='%s'", self->id);
+
+ if (self->kind == E_UI_ELEMENT_KIND_MENU) {
+ if (self->data.menu.is_popup)
+ g_string_append (str, " is-popup='true'");
+ } else if (self->kind == E_UI_ELEMENT_KIND_SUBMENU) {
+ if (self->data.submenu.action)
+ e_util_markup_append_escaped (str, " action='%s'", self->data.submenu.action);
+ } else if (self->kind == E_UI_ELEMENT_KIND_HEADERBAR) {
+ if (self->data.headerbar.use_gtk_type)
+ g_string_append (str, " type='gtk'");
+ } else if (self->kind == E_UI_ELEMENT_KIND_TOOLBAR) {
+ if (self->data.toolbar.primary)
+ g_string_append (str, " primary='true'");
+ } else if (self->kind == E_UI_ELEMENT_KIND_ITEM) {
+ if (self->data.item.action)
+ e_util_markup_append_escaped (str, " action='%s'", self->data.item.action);
+ if (self->data.item.group)
+ e_util_markup_append_escaped (str, " group='%s'", self->data.item.group);
+ if (self->data.item.label_priority != G_MAXUINT32)
+ g_string_append_printf (str, " label_priority='%u'", self->data.item.label_priority);
+ if (self->data.item.icon_only != G_MAXINT32)
+ g_string_append_printf (str, " icon_only='%s'", self->data.item.icon_only ? "true" : "false");
+ if (self->data.item.text_only != G_MAXINT32)
+ g_string_append_printf (str, " text_only='%s'", self->data.item.text_only ? "true" : "false");
+ if (self->data.item.important)
+ g_string_append (str, " important='true'");
+ if (self->data.item.css_classes)
+ g_string_append_printf (str, " css_classes='%s'", self->data.item.css_classes);
+ if (self->data.item.order)
+ g_string_append_printf (str, " order='%d'", self->data.item.order);
+ }
+
+ if (self->children) {
+ guint ii;
+
+ g_string_append_c (str, '>');
+
+ if (indent >= 0)
+ g_string_append_c (str, '\n');
+
+ for (ii = 0; ii < self->children->len; ii++) {
+ const EUIElement *subelem = g_ptr_array_index (self->children, ii);
+ e_ui_element_export (subelem, str, indent >= 0 ? indent + 1 : indent);
+ }
+
+ if (indent >= 0)
+ g_string_append_printf (str, "%*s", indent * 2, "");
+
+ g_string_append_printf (str, "%s>", e_ui_element_kind_to_string (self->kind));
+ } else {
+ g_string_append (str, "/>");
+ }
+
+ if (indent >= 0)
+ g_string_append_c (str, '\n');
+}
+
+/* menu element functions */
+
+/**
+ * e_ui_element_menu_get_is_popup:
+ * @self: an #EUIElement
+ *
+ * Gets a whether a menu is a popup (context) menu. Such menu hides actions
+ * when they are insensitive. The default value is %FALSE.
+ *
+ * This can be called only on elements of kind %E_UI_ELEMENT_KIND_MENU.
+ *
+ * Returns: whether the menu is a popup menu
+ *
+ * Since: 3.56
+ **/
+gboolean
+e_ui_element_menu_get_is_popup (const EUIElement *self)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (self->kind == E_UI_ELEMENT_KIND_MENU, FALSE);
+
+ return self->data.menu.is_popup;
+}
+
+/* submenu element functions */
+
+/**
+ * e_ui_element_submenu_get_action:
+ * @self: an #EUIElement
+ *
+ * Gets a submenu action name.
+ *
+ * This can be called only on elements of kind %E_UI_ELEMENT_KIND_SUBMENU.
+ *
+ * Returns: an action name for the submenu element
+ *
+ * Since: 3.56
+ **/
+const gchar *
+e_ui_element_submenu_get_action (const EUIElement *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (self->kind == E_UI_ELEMENT_KIND_SUBMENU, NULL);
+
+ return self->data.submenu.action;
+}
+
+/* headerbar element functions */
+
+/**
+ * e_ui_element_headerbar_get_use_gtk_type:
+ * @self: an #EUIElement
+ *
+ * Gets whether a #GtkHeaderBar should be created, instead of an #EHeaderBar.
+ *
+ * This can be called only on elements of kind %E_UI_ELEMENT_KIND_HEADERBAR.
+ *
+ * Returns: whether create #GtkHeaderBar
+ *
+ * Since: 3.56
+ **/
+gboolean
+e_ui_element_headerbar_get_use_gtk_type (const EUIElement *self)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (self->kind == E_UI_ELEMENT_KIND_HEADERBAR, FALSE);
+
+ return self->data.headerbar.use_gtk_type;
+}
+
+/* toolbar element functions */
+
+/**
+ * e_ui_element_toolbar_get_primary:
+ * @self: an #EUIElement
+ *
+ * Gets whether the toolbar is a primary toolbar.
+ *
+ * This can be called only on elements of kind %E_UI_ELEMENT_KIND_TOOLBAR.
+ *
+ * Returns: whether the toolbar is a primary toolbar
+ *
+ * Since: 3.56
+ **/
+gboolean
+e_ui_element_toolbar_get_primary (const EUIElement *self)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (self->kind == E_UI_ELEMENT_KIND_TOOLBAR, FALSE);
+
+ return self->data.toolbar.primary;
+}
+
+/* item element functions */
+
+/**
+ * e_ui_element_item_get_label_priority:
+ * @self: an #EUIElement
+ *
+ * Gets a label priority value for an item element. It is set only
+ * for headerbar items.
+ *
+ * This can be called only on elements of kind %E_UI_ELEMENT_KIND_ITEM.
+ *
+ * Returns: a label priority
+ *
+ * Since: 3.56
+ **/
+guint
+e_ui_element_item_get_label_priority (const EUIElement *self)
+{
+ g_return_val_if_fail (self != NULL, 0);
+ g_return_val_if_fail (self->kind == E_UI_ELEMENT_KIND_ITEM, 0);
+
+ return self->data.item.label_priority;
+}
+
+/**
+ * e_ui_element_item_get_order:
+ * @self: an #EUIElement
+ *
+ * Gets order of the button in the header bar. Items with lower number
+ * are added before items with higher number. The default value is zero,
+ * meaning use the order as added through the .eui file.
+ *
+ * This can be called only on elements of kind %E_UI_ELEMENT_KIND_ITEM.
+ *
+ * Returns: order index of the header bar item
+ *
+ * Since: 3.56
+ **/
+gint
+e_ui_element_item_get_order (const EUIElement *self)
+{
+ g_return_val_if_fail (self != NULL, 0);
+ g_return_val_if_fail (self->kind == E_UI_ELEMENT_KIND_ITEM, 0);
+
+ return self->data.item.order;
+}
+
+/**
+ * e_ui_element_item_get_icon_only_is_set:
+ * @self: an #EUIElement
+ *
+ * Gets whether an item has set icon-only property. The default value is %FALSE.
+ * It is set only for headerbar items. Use e_ui_element_item_get_icon_only()
+ * to get the actual value.
+ *
+ * This can be called only on elements of kind %E_UI_ELEMENT_KIND_ITEM.
+ *
+ * Returns: whether the icon-only property is set
+ *
+ * Since: 3.56
+ **/
+gboolean
+e_ui_element_item_get_icon_only_is_set (const EUIElement *self)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (self->kind == E_UI_ELEMENT_KIND_ITEM, FALSE);
+
+ return self->data.item.icon_only != G_MAXINT32;
+}
+
+/**
+ * e_ui_element_item_get_icon_only:
+ * @self: an #EUIElement
+ *
+ * Gets whether an item may show only icon. The default value is %FALSE.
+ * It is set only for headerbar items. Use e_ui_element_item_get_icon_only_is_set()
+ * to check whether the value had been set.
+ *
+ * This can be called only on elements of kind %E_UI_ELEMENT_KIND_ITEM.
+ *
+ * Returns: whether to show only icon
+ *
+ * Since: 3.56
+ **/
+gboolean
+e_ui_element_item_get_icon_only (const EUIElement *self)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (self->kind == E_UI_ELEMENT_KIND_ITEM, FALSE);
+
+ if (!e_ui_element_item_get_icon_only_is_set (self))
+ return FALSE;
+
+ return self->data.item.icon_only != 0;
+}
+
+/**
+ * e_ui_element_item_get_text_only_is_set:
+ * @self: an #EUIElement
+ *
+ * Gets whether an item has set text-only property. The default value is %FALSE.
+ * Use e_ui_element_item_get_text_only() to get the actual value.
+ *
+ * This can be called only on elements of kind %E_UI_ELEMENT_KIND_ITEM.
+ *
+ * Returns: whether the text-only property is set
+ *
+ * Since: 3.56
+ **/
+gboolean
+e_ui_element_item_get_text_only_is_set (const EUIElement *self)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (self->kind == E_UI_ELEMENT_KIND_ITEM, FALSE);
+
+ return self->data.item.text_only != G_MAXINT32;
+}
+
+/**
+ * e_ui_element_item_get_text_only:
+ * @self: an #EUIElement
+ *
+ * Gets whether an item may show only text. The default value is %FALSE.
+ * Use e_ui_element_item_get_text_only_is_set()
+ * to check whether the value had been set.
+ *
+ * This can be called only on elements of kind %E_UI_ELEMENT_KIND_ITEM.
+ *
+ * Returns: whether to show only text
+ *
+ * Since: 3.56
+ **/
+gboolean
+e_ui_element_item_get_text_only (const EUIElement *self)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (self->kind == E_UI_ELEMENT_KIND_ITEM, FALSE);
+
+ if (!e_ui_element_item_get_text_only_is_set (self))
+ return FALSE;
+
+ return self->data.item.text_only != 0;
+}
+
+/**
+ * e_ui_element_item_get_important:
+ * @self: an #EUIElement
+ *
+ * Gets whether an item is important. The default value is %FALSE.
+ * It's used for items inside toolbars.
+ *
+ * This can be called only on elements of kind %E_UI_ELEMENT_KIND_ITEM.
+ *
+ * Returns: whether the item is important
+ *
+ * Since: 3.56
+ **/
+gboolean
+e_ui_element_item_get_important (const EUIElement *self)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (self->kind == E_UI_ELEMENT_KIND_ITEM, FALSE);
+
+ return self->data.item.important;
+}
+
+/**
+ * e_ui_element_item_get_css_classes:
+ * @self: an #EUIElement
+ *
+ * Gets a space-separated list of CSS classes to add on the item.
+ *
+ * This can be called only on elements of kind %E_UI_ELEMENT_KIND_ITEM.
+ *
+ * Returns: (nullable): a space-separated list of CSS classes to add on a GtkWidget,
+ * or %NULL, when not set
+ *
+ * Since: 3.56
+ **/
+const gchar *
+e_ui_element_item_get_css_classes (const EUIElement *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (self->kind == E_UI_ELEMENT_KIND_ITEM, NULL);
+
+ return self->data.item.css_classes;
+}
+
+/**
+ * e_ui_element_item_get_action:
+ * @self: an #EUIElement
+ *
+ * Gets a item action name.
+ *
+ * This can be called only on elements of kind %E_UI_ELEMENT_KIND_ITEM.
+ *
+ * Returns: an action name
+ *
+ * Since: 3.56
+ **/
+const gchar *
+e_ui_element_item_get_action (const EUIElement *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (self->kind == E_UI_ELEMENT_KIND_ITEM, NULL);
+
+ return self->data.item.action;
+}
+
+/**
+ * e_ui_element_item_get_group:
+ * @self: an #EUIElement
+ *
+ * Gets a radio group name.
+ *
+ * This can be called only on elements of kind %E_UI_ELEMENT_KIND_ITEM.
+ *
+ * Returns: (nullable): a radio group name of an item, or %NULL,
+ * when the item is not part of any radio group
+ *
+ * Since: 3.56
+ **/
+const gchar *
+e_ui_element_item_get_group (const EUIElement *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (self->kind == E_UI_ELEMENT_KIND_ITEM, NULL);
+
+ return self->data.item.group;
+}
+
+/* ***************************************************************** */
+
+struct _EUIParser {
+ GObject parent;
+
+ EUIElement *root;
+};
+
+enum {
+ SIGNAL_CHANGED,
+ N_SIGNALS
+};
+
+static guint signals[N_SIGNALS] = { 0, };
+
+G_DEFINE_TYPE (EUIParser, e_ui_parser, G_TYPE_OBJECT)
+
+static void
+e_ui_parser_finalize (GObject *object)
+{
+ EUIParser *self = E_UI_PARSER (object);
+
+ g_clear_pointer (&self->root, e_ui_element_free);
+
+ G_OBJECT_CLASS (e_ui_parser_parent_class)->finalize (object);
+}
+
+static void
+e_ui_parser_class_init (EUIParserClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = e_ui_parser_finalize;
+
+ /**
+ * EUIParser::changed:
+ * @parser: an #EUIParser
+ *
+ * Emitted when the content of the @parser changes.
+ *
+ * Since: 3.56
+ **/
+ signals[SIGNAL_CHANGED] = g_signal_new ("changed",
+ E_TYPE_UI_PARSER,
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0,
+ G_TYPE_NONE);
+}
+
+static void
+e_ui_parser_init (EUIParser *self)
+{
+}
+
+/**
+ * e_ui_parser_new:
+ *
+ * Creates a new #EUIParser. Use g_object_unref() to free it, when no longer needed.
+ *
+ * Returns: (transfer full): a new #EUIParser
+ *
+ * Since: 3.56
+ **/
+EUIParser *
+e_ui_parser_new (void)
+{
+ return g_object_new (E_TYPE_UI_PARSER, NULL);
+}
+
+/**
+ * e_ui_parser_merge_file:
+ * @self: an #EUIParser
+ * @filename: a filename to merge
+ * @error: an output location to store a #GError at, or %NULL
+ *
+ * Adds content of the @filename into the UI definition. Items with
+ * the same identifier are reused, allowing to add new items into
+ * existing hierarchy.
+ *
+ * Returns: whether could successfully parse the file content. On failure,
+ * the @error is set.
+ *
+ * Since: 3.56
+ **/
+gboolean
+e_ui_parser_merge_file (EUIParser *self,
+ const gchar *filename,
+ GError **error)
+{
+ gchar *full_filename = NULL;
+ gchar *content = NULL;
+ gsize content_len = 0;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_UI_PARSER (self), FALSE);
+ g_return_val_if_fail (filename != NULL, FALSE);
+
+ if (!strchr (filename, G_DIR_SEPARATOR))
+ full_filename = g_build_filename (EVOLUTION_UIDIR, filename, NULL);
+
+ success = g_file_get_contents (full_filename ? full_filename : filename, &content, &content_len, error);
+
+ g_free (full_filename);
+
+ if (!success)
+ return FALSE;
+
+ success = e_ui_parser_merge_data (self, content, content_len, error);
+
+ g_free (content);
+
+ return success;
+}
+
+static EUIElement * /* (transfer full) */
+ui_parser_xml_read_item_element (const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gboolean for_headerbar,
+ gboolean for_menu,
+ GError **error)
+{
+ EUIElement *ui_elem;
+ const gchar *action = NULL;
+ const gchar *group = NULL;
+ const gchar *label_priority = NULL;
+ const gchar *icon_only = NULL;
+ const gchar *text_only = NULL;
+ const gchar *important = NULL;
+ const gchar *css_classes = NULL;
+ const gchar *order = NULL;
+ gint order_index = 0;
+
+ if (for_headerbar) {
+ if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
+ G_MARKUP_COLLECT_STRING, "action", &action,
+ G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "group", &group,
+ G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "css_classes", &css_classes,
+ G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "label_priority", &label_priority,
+ G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "icon_only", &icon_only,
+ G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "order", &order,
+ G_MARKUP_COLLECT_INVALID))
+ return NULL;
+
+ if (order && *order) {
+ gchar *endptr = NULL;
+
+ order_index = (gint) g_ascii_strtoll (order, &endptr, 10);
+ if (!order_index && endptr == order) {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Element <%s> can have optional 'order' attribute of type integer, but the value '%s' is not a valid integer", element_name, order);
+ return NULL;
+ }
+ } else {
+ order = NULL;
+ }
+ } else if (for_menu) {
+ if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
+ G_MARKUP_COLLECT_STRING, "action", &action,
+ G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "group", &group,
+ G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "css_classes", &css_classes,
+ G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "text_only", &text_only,
+ G_MARKUP_COLLECT_INVALID))
+ return NULL;
+ } else {
+ if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
+ G_MARKUP_COLLECT_STRING, "action", &action,
+ G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "group", &group,
+ G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "css_classes", &css_classes,
+ G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "important", &important,
+ G_MARKUP_COLLECT_INVALID))
+ return NULL;
+ }
+
+ if (icon_only && *icon_only &&
+ g_strcmp0 (icon_only, "true") != 0 &&
+ g_strcmp0 (icon_only, "false") != 0) {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "The 'icon-only' expects only 'true' or 'false' value, but '%s' was provided instead", icon_only);
+ return NULL;
+ }
+
+ if (text_only && *text_only &&
+ g_strcmp0 (text_only, "true") != 0 &&
+ g_strcmp0 (text_only, "false") != 0) {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "The 'text-only' expects only 'true' or 'false' value, but '%s' was provided instead", text_only);
+ return NULL;
+ }
+
+ if (important && *important &&
+ g_strcmp0 (important, "true") != 0 &&
+ g_strcmp0 (important, "false") != 0) {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "The 'important' expects only 'true' or 'false' value, but '%s' was provided instead", important);
+ return NULL;
+ }
+
+ ui_elem = e_ui_element_new (E_UI_ELEMENT_KIND_ITEM, NULL);
+ ui_elem->data.item.css_classes = e_util_strdup_strip (css_classes);
+ ui_elem->data.item.action = e_util_strdup_strip (action);
+ ui_elem->data.item.group = e_util_strdup_strip (group);
+ ui_elem->data.item.important = g_strcmp0 (important, "true") == 0;
+
+ if (icon_only && *icon_only)
+ ui_elem->data.item.icon_only = g_strcmp0 (icon_only, "true") == 0 ? 1 : 0;
+
+ if (text_only && *text_only)
+ ui_elem->data.item.text_only = g_strcmp0 (text_only, "true") == 0 ? 1 : 0;
+
+ if (label_priority && *label_priority) {
+ guint64 value;
+
+ value = g_ascii_strtoull (label_priority, NULL, 10);
+ ui_elem->data.item.label_priority = value;
+ }
+
+ if (order)
+ ui_elem->data.item.order = order_index;
+
+ return ui_elem;
+}
+
+typedef struct _ParseData {
+ EUIParser *self;
+ GSList *elems_stack;
+ gboolean changed;
+} ParseData;
+
+/* returns whether handled, not whether succeeded */
+static gboolean
+ui_parser_xml_handle_item_separator_placeholder (ParseData *pd,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gboolean item_for_headerbar,
+ gboolean item_for_menu,
+ GError **error)
+{
+ EUIElement *ui_elem = NULL, *parent_ui_elem;
+ gboolean is_existing = FALSE;
+
+ parent_ui_elem = pd->elems_stack->data;
+
+ if (g_strcmp0 (element_name, "item") == 0) {
+ ui_elem = ui_parser_xml_read_item_element (element_name, attribute_names, attribute_values, item_for_headerbar, item_for_menu, error);
+ if (!ui_elem)
+ return TRUE;
+ } else if (g_strcmp0 (element_name, "separator") == 0) {
+ ui_elem = e_ui_element_new (E_UI_ELEMENT_KIND_SEPARATOR, NULL);
+ } else if (g_strcmp0 (element_name, "placeholder") == 0) {
+ const gchar *id = NULL;
+
+ if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
+ G_MARKUP_COLLECT_STRING, "id", &id,
+ G_MARKUP_COLLECT_INVALID))
+ return TRUE;
+
+ ui_elem = e_ui_element_get_child_by_id (parent_ui_elem, id);
+ if (ui_elem) {
+ if (ui_elem->kind != E_UI_ELEMENT_KIND_PLACEHOLDER) {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Duplicate element id \"%s\" of different kind detected, existing kind <%s>, requested kind ",
+ id, e_ui_element_kind_to_string (ui_elem->kind));
+ return TRUE;
+ }
+
+ is_existing = TRUE;
+ } else {
+ ui_elem = e_ui_element_new (E_UI_ELEMENT_KIND_PLACEHOLDER, id);
+ }
+ } else {
+ return FALSE;
+ }
+
+ if (!is_existing) {
+ e_ui_element_add_child (parent_ui_elem, ui_elem);
+ pd->changed = TRUE;
+ }
+
+ if (ui_elem->kind == E_UI_ELEMENT_KIND_PLACEHOLDER)
+ pd->elems_stack = g_slist_prepend (pd->elems_stack, ui_elem);
+
+ return TRUE;
+}
+
+static void
+ui_parser_xml_handle_menu_submenu_start_element (ParseData *pd,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ GError **error)
+{
+ EUIElement *ui_elem = NULL, *parent_ui_elem;
+ const gchar *id = NULL, *action = NULL;
+
+ if (ui_parser_xml_handle_item_separator_placeholder (pd, element_name, attribute_names, attribute_values, FALSE, TRUE, error))
+ return;
+
+ if (g_strcmp0 (element_name, "submenu") == 0) {
+ if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
+ G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "id", &id,
+ G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "action", &action,
+ G_MARKUP_COLLECT_INVALID))
+ return;
+ } else {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+ "Unknown element <%s>, expected , - , or ", element_name);
+ return;
+ }
+
+ /* to not need to specify both 'id' and 'action' attributes */
+ if (action && !id)
+ id = action;
+
+ parent_ui_elem = pd->elems_stack->data;
+ if (id)
+ ui_elem = e_ui_element_get_child_by_id (parent_ui_elem, id);
+ if (ui_elem) {
+ if (ui_elem->kind != E_UI_ELEMENT_KIND_SUBMENU) {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Duplicate element id \"%s\" of different kind detected, existing kind <%s>, requested kind ",
+ id, e_ui_element_kind_to_string (ui_elem->kind));
+ return;
+ }
+ } else if (!action) {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Element <%s> requires 'action' attribute", element_name);
+ return;
+ } else {
+ ui_elem = e_ui_element_new (E_UI_ELEMENT_KIND_SUBMENU, id);
+ e_ui_element_add_child (parent_ui_elem, ui_elem);
+ pd->changed = TRUE;
+
+ if (action)
+ ui_elem->data.submenu.action = e_util_strdup_strip (action);
+ }
+
+ pd->elems_stack = g_slist_prepend (pd->elems_stack, ui_elem);
+}
+
+static void
+ui_parser_xml_handle_subroot_start_element (ParseData *pd,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ GError **error)
+{
+ EUIElementKind elem_kind = E_UI_ELEMENT_KIND_UNKNOWN;
+ EUIElement *ui_elem, *parent_ui_elem;
+ const gchar *id = NULL;
+ const gchar *type = NULL;
+ const gchar *primary = NULL;
+ gboolean is_popup = FALSE;
+
+ if (g_strcmp0 (element_name, "headerbar") == 0) {
+ elem_kind = E_UI_ELEMENT_KIND_HEADERBAR;
+ } else if (g_strcmp0 (element_name, "menu") == 0) {
+ elem_kind = E_UI_ELEMENT_KIND_MENU;
+ } else if (g_strcmp0 (element_name, "toolbar") == 0) {
+ elem_kind = E_UI_ELEMENT_KIND_TOOLBAR;
+ } else {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+ "Unknown element <%s>, expected , or ", element_name);
+ return;
+ }
+
+ if (elem_kind == E_UI_ELEMENT_KIND_HEADERBAR) {
+ if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
+ G_MARKUP_COLLECT_STRING, "id", &id,
+ G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "type", &type,
+ G_MARKUP_COLLECT_INVALID))
+ return;
+
+ if (type && *type && g_strcmp0 (type, "gtk") != 0) {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Element <%s> can have optional 'type' attribute of value 'gtk' only, but type '%s' provided", element_name, type);
+ return;
+ }
+ } else if (elem_kind == E_UI_ELEMENT_KIND_TOOLBAR) {
+ if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
+ G_MARKUP_COLLECT_STRING, "id", &id,
+ G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "primary", &primary,
+ G_MARKUP_COLLECT_INVALID))
+ return;
+
+ if (primary && *primary && g_strcmp0 (primary, "true") != 0 && g_strcmp0 (primary, "false") != 0) {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Element <%s> can have optional 'primary' attribute of value 'true' or 'false' only, but value '%s' provided", element_name, primary);
+ return;
+ }
+ } else { /* E_UI_ELEMENT_KIND_MENU */
+ const gchar *is_popup_str = NULL;
+
+ if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
+ G_MARKUP_COLLECT_STRING, "id", &id,
+ G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "is-popup", &is_popup_str,
+ G_MARKUP_COLLECT_INVALID))
+ return;
+
+ if (is_popup_str && *is_popup_str && g_strcmp0 (is_popup_str, "true") != 0 && g_strcmp0 (is_popup_str, "false") != 0) {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Element <%s> can have optional 'is-popup' attribute of value 'true' or 'false' only, but value '%s' provided", element_name, is_popup_str);
+ return;
+ }
+
+ is_popup = g_strcmp0 (is_popup_str, "true") == 0;
+ }
+
+ parent_ui_elem = pd->elems_stack->data;
+ ui_elem = e_ui_element_get_child_by_id (parent_ui_elem, id);
+ if (ui_elem) {
+ if (ui_elem->kind != elem_kind) {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Duplicate element id \"%s\" of different kind detected, existing kind <%s>, requested kind <%s>",
+ id, e_ui_element_kind_to_string (ui_elem->kind), e_ui_element_kind_to_string (elem_kind));
+ return;
+ }
+ } else {
+ ui_elem = e_ui_element_new (elem_kind, id);
+
+ if (type && *type && g_strcmp0 (type, "gtk") == 0)
+ ui_elem->data.headerbar.use_gtk_type = TRUE;
+ if (primary && *primary && g_strcmp0 (primary, "true") == 0)
+ ui_elem->data.toolbar.primary = TRUE;
+ if (is_popup)
+ ui_elem->data.menu.is_popup = TRUE;
+
+ e_ui_element_add_child (parent_ui_elem, ui_elem);
+ pd->changed = TRUE;
+ }
+
+ pd->elems_stack = g_slist_prepend (pd->elems_stack, ui_elem);
+}
+
+static gint
+ui_parser_sort_by_order_cmp (gconstpointer aa,
+ gconstpointer bb)
+{
+ const EUIElement *elem1 = *((EUIElement **) aa);
+ const EUIElement *elem2 = *((EUIElement **) bb);
+
+ if (!elem1 || !elem2) {
+ if (elem1)
+ return 1;
+ if (elem2)
+ return -1;
+ return 0;
+ }
+
+ return e_ui_element_item_get_order (elem1) - e_ui_element_item_get_order (elem2);
+}
+
+static void
+ui_parser_xml_handle_start_end (ParseData *pd,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ GError **error)
+{
+ EUIElementKind elem_kind = E_UI_ELEMENT_KIND_UNKNOWN;
+ EUIElement *ui_elem = NULL, *parent_ui_elem;
+ guint ii;
+
+ if (g_strcmp0 (element_name, "start") == 0) {
+ elem_kind = E_UI_ELEMENT_KIND_START;
+ } else if (g_strcmp0 (element_name, "end") == 0) {
+ elem_kind = E_UI_ELEMENT_KIND_END;
+ } else {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+ "Unknown element <%s>, expected or ", element_name);
+ return;
+ }
+
+ if (attribute_names != NULL && attribute_names[0]) {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
+ "Element <%s> doesn't have any attributes", element_name);
+ return;
+ }
+
+ parent_ui_elem = pd->elems_stack->data;
+
+ for (ii = 0; ii < e_ui_element_get_n_children (parent_ui_elem); ii++) {
+ ui_elem = e_ui_element_get_child (parent_ui_elem, ii);
+ if (ui_elem && ui_elem->kind == elem_kind)
+ break;
+ else
+ ui_elem = NULL;
+ }
+
+ if (!ui_elem) {
+ ui_elem = e_ui_element_new (elem_kind, e_ui_element_kind_to_string (elem_kind));
+ e_ui_element_add_child (parent_ui_elem, ui_elem);
+ pd->changed = TRUE;
+ }
+
+ pd->elems_stack = g_slist_prepend (pd->elems_stack, ui_elem);
+}
+
+static void
+ui_parser_xml_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ ParseData *pd = user_data;
+ EUIElement *ui_elem;
+ EUIElementKind parent_kind;
+ GSList *stack_link;
+
+ if (!pd->elems_stack) {
+ if (g_strcmp0 (element_name, "eui") != 0) {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+ "Unknown element <%s>, expected ", element_name);
+ return;
+ }
+
+ if (!pd->self->root) {
+ pd->self->root = e_ui_element_new (E_UI_ELEMENT_KIND_ROOT, NULL);
+ pd->changed = TRUE;
+ }
+
+ pd->elems_stack = g_slist_prepend (pd->elems_stack, pd->self->root);
+
+ return;
+ }
+
+ ui_elem = pd->elems_stack->data;
+ parent_kind = ui_elem->kind;
+
+ stack_link = pd->elems_stack;
+
+ while (parent_kind == E_UI_ELEMENT_KIND_PLACEHOLDER && stack_link && stack_link->next) {
+ EUIElement *parent_ui_elem;
+
+ parent_ui_elem = stack_link->next->data;
+ parent_kind = parent_ui_elem->kind;
+
+ stack_link = stack_link->next;
+ }
+
+ if (parent_kind == E_UI_ELEMENT_KIND_ROOT) {
+ ui_parser_xml_handle_subroot_start_element (pd, element_name, attribute_names, attribute_values, error);
+ } else if (parent_kind == E_UI_ELEMENT_KIND_HEADERBAR) {
+ ui_parser_xml_handle_start_end (pd, element_name, attribute_names, attribute_values, error);
+ } else if (parent_kind == E_UI_ELEMENT_KIND_START || parent_kind == E_UI_ELEMENT_KIND_END) {
+ if (!ui_parser_xml_handle_item_separator_placeholder (pd, element_name, attribute_names, attribute_values, TRUE, FALSE, error)) {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+ "Unknown element <%s>, expected
- , or ", element_name);
+ }
+ } else if (parent_kind == E_UI_ELEMENT_KIND_TOOLBAR) {
+ if (!ui_parser_xml_handle_item_separator_placeholder (pd, element_name, attribute_names, attribute_values, FALSE, FALSE, error)) {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+ "Unknown element <%s>, expected
- , or ", element_name);
+ }
+ } else if (parent_kind == E_UI_ELEMENT_KIND_MENU ||
+ parent_kind == E_UI_ELEMENT_KIND_SUBMENU) {
+ ui_parser_xml_handle_menu_submenu_start_element (pd, element_name, attribute_names, attribute_values, error);
+ } else {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+ "Unknown element <%s> under <%s>", element_name, e_ui_element_kind_to_string (parent_kind));
+ }
+}
+
+static void
+ui_parser_xml_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ ParseData *pd = user_data;
+
+ if (g_strcmp0 (element_name, "eui") == 0) {
+ if (!pd->elems_stack) {
+ g_set_error_literal (error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, "Ends without root element");
+ } else if (g_slist_length (pd->elems_stack) != 1) {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ "Expected end with single elem stack, but the stack has %u items",
+ g_slist_length (pd->elems_stack));
+ } else if (pd->elems_stack->data != pd->self->root) {
+ g_set_error_literal (error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, "Ends with incorrect stack top");
+ } else {
+ pd->elems_stack = g_slist_remove (pd->elems_stack, pd->elems_stack->data);
+ }
+ } else if (g_strcmp0 (element_name, "item") == 0 ||
+ g_strcmp0 (element_name, "separator") == 0) {
+ } else if (pd->elems_stack) {
+ EUIElementKind expect_kind = E_UI_ELEMENT_KIND_UNKNOWN;
+
+ if (g_strcmp0 (element_name, "headerbar") == 0)
+ expect_kind = E_UI_ELEMENT_KIND_HEADERBAR;
+ else if (g_strcmp0 (element_name, "toolbar") == 0)
+ expect_kind = E_UI_ELEMENT_KIND_TOOLBAR;
+ else if (g_strcmp0 (element_name, "menu") == 0)
+ expect_kind = E_UI_ELEMENT_KIND_MENU;
+ else if (g_strcmp0 (element_name, "submenu") == 0)
+ expect_kind = E_UI_ELEMENT_KIND_SUBMENU;
+ else if (g_strcmp0 (element_name, "placeholder") == 0)
+ expect_kind = E_UI_ELEMENT_KIND_PLACEHOLDER;
+ else if (g_strcmp0 (element_name, "start") == 0)
+ expect_kind = E_UI_ELEMENT_KIND_START;
+ else if (g_strcmp0 (element_name, "end") == 0)
+ expect_kind = E_UI_ELEMENT_KIND_END;
+ else
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, "Unexpected element end <%s>", element_name);
+
+ if (expect_kind != E_UI_ELEMENT_KIND_UNKNOWN) {
+ EUIElement *ui_elem = pd->elems_stack->data;
+
+ if (ui_elem->kind == expect_kind)
+ pd->elems_stack = g_slist_remove (pd->elems_stack, pd->elems_stack->data);
+ else
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, "Unexpected element <%s> on stack, but ended was <%s>",
+ e_ui_element_kind_to_string (ui_elem->kind), element_name);
+
+ if (expect_kind == E_UI_ELEMENT_KIND_START || expect_kind == E_UI_ELEMENT_KIND_END) {
+ guint ii;
+
+ for (ii = 0; ii < e_ui_element_get_n_children (ui_elem); ii++) {
+ EUIElement *child = e_ui_element_get_child (ui_elem, ii);
+
+ if (child && e_ui_element_item_get_order (child) != 0)
+ break;
+ }
+
+ /* when one element has non-zero order */
+ if (ii < e_ui_element_get_n_children (ui_elem))
+ g_ptr_array_sort (ui_elem->children, ui_parser_sort_by_order_cmp);
+ }
+ }
+ }
+}
+
+/**
+ * e_ui_parser_merge_data:
+ * @self: an #EUIParser
+ * @data: a data to merge
+ * @data_len: length of the @data, or -1, when NUL-terminated
+ * @error: an output location to store a #GError at, or %NULL
+ *
+ * Adds content of the @data into the UI definition. Items with
+ * the same identifier are reused, allowing to add new items into
+ * existing hierarchy.
+ *
+ * Returns: whether could successfully parse the file content. On failure,
+ * the @error is set.
+ *
+ * Since: 3.56
+ **/
+gboolean
+e_ui_parser_merge_data (EUIParser *self,
+ const gchar *data,
+ gssize data_len,
+ GError **error)
+{
+ static GMarkupParser xml_parser = {
+ ui_parser_xml_start_element,
+ ui_parser_xml_end_element,
+ NULL, /* text */
+ NULL, /* passthrough */
+ NULL /* error */
+ };
+ GMarkupParseContext *context;
+ ParseData pd = { NULL, };
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_UI_PARSER (self), FALSE);
+ g_return_val_if_fail (data != NULL, FALSE);
+
+ pd.self = self;
+ pd.elems_stack = NULL;
+ pd.changed = FALSE;
+
+ context = g_markup_parse_context_new (&xml_parser, 0, &pd, NULL);
+
+ success = g_markup_parse_context_parse (context, data, data_len, error) &&
+ g_markup_parse_context_end_parse (context, error);
+
+ g_markup_parse_context_free (context);
+
+ if (pd.changed)
+ g_signal_emit (self, signals[SIGNAL_CHANGED], 0, NULL);
+
+ return success;
+}
+
+/**
+ * e_ui_parser_clear:
+ * @self: an #EUIParser
+ *
+ * Removes all content of the @self.
+ *
+ * Since: 3.56
+ **/
+void
+e_ui_parser_clear (EUIParser *self)
+{
+ g_return_if_fail (E_IS_UI_PARSER (self));
+
+ if (self->root) {
+ g_clear_pointer (&self->root, e_ui_element_free);
+ g_signal_emit (self, signals[SIGNAL_CHANGED], 0, NULL);
+ }
+}
+
+/**
+ * e_ui_parser_get_root:
+ * @self: an #EUIParser
+ *
+ * Gets the root element of the @self.
+ *
+ * Returns: (nullable) (transfer none): a root #EUIElement, or %NULL, when nothing was parsed yet
+ *
+ * Since: 3.56
+ **/
+EUIElement *
+e_ui_parser_get_root (EUIParser *self)
+{
+ g_return_val_if_fail (E_IS_UI_PARSER (self), NULL);
+
+ return self->root;
+}
+
+/**
+ * e_ui_parser_export:
+ * @self: an #EUIParser
+ * @flags: a bit-or of #EUIParserExportFlags flags
+ *
+ * Exports the content of the @self in a format suitable for e_ui_parser_merge_file()
+ * and e_ui_parser_merge_data().
+ *
+ * Free the returned string with g_free(), when no longer needed.
+ *
+ * Returns: (transfer full) (nullable): a string representation of the @self content,
+ * or %NULL, when the @self is empty.
+ *
+ * Since: 3.56
+ **/
+gchar *
+e_ui_parser_export (EUIParser *self,
+ EUIParserExportFlags flags)
+{
+ GString *str;
+
+ g_return_val_if_fail (E_IS_UI_PARSER (self), NULL);
+
+ if (!self->root)
+ return NULL;
+
+ str = g_string_new ("");
+
+ e_ui_element_export (self->root, str, (flags & E_UI_PARSER_EXPORT_FLAG_INDENT) ? 0 : -1);
+
+ if (!(flags & E_UI_PARSER_EXPORT_FLAG_INDENT))
+ g_string_append_c (str, '\n');
+
+ return g_string_free (str, FALSE);
+}
diff --git a/src/e-util/e-ui-parser.h b/src/e-util/e-ui-parser.h
new file mode 100644
index 0000000000..1024e28cf0
--- /dev/null
+++ b/src/e-util/e-ui-parser.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * SPDX-FileCopyrightText: (C) 2024 Red Hat (www.redhat.com>
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only should be included directly."
+#endif
+
+#ifndef E_UI_PARSER_H
+#define E_UI_PARSER_H
+
+#include
+
+#include
+
+G_BEGIN_DECLS
+
+#define E_TYPE_UI_PARSER e_ui_parser_get_type ()
+
+G_DECLARE_FINAL_TYPE (EUIParser, e_ui_parser, E, UI_PARSER, GObject)
+
+typedef struct _EUIElement EUIElement;
+
+/* generic EUIElement functions, usable for any element */
+EUIElementKind e_ui_element_get_kind (const EUIElement *self);
+const gchar * e_ui_element_get_id (const EUIElement *self);
+guint e_ui_element_get_n_children (const EUIElement *self);
+EUIElement * e_ui_element_get_child (EUIElement *self,
+ guint index);
+EUIElement * e_ui_element_get_child_by_id (EUIElement *self,
+ const gchar *id);
+/* menu element functions */
+gboolean e_ui_element_menu_get_is_popup (const EUIElement *self);
+/* submenu element functions */
+const gchar * e_ui_element_submenu_get_action (const EUIElement *self);
+/* headerbar element functions */
+gboolean e_ui_element_headerbar_get_use_gtk_type
+ (const EUIElement *self);
+/* toolbar element functions */
+gboolean e_ui_element_toolbar_get_primary(const EUIElement *self);
+/* item element functions */
+guint e_ui_element_item_get_label_priority
+ (const EUIElement *self);
+gint e_ui_element_item_get_order (const EUIElement *self);
+gboolean e_ui_element_item_get_icon_only_is_set
+ (const EUIElement *self);
+gboolean e_ui_element_item_get_icon_only (const EUIElement *self);
+gboolean e_ui_element_item_get_text_only_is_set
+ (const EUIElement *self);
+gboolean e_ui_element_item_get_text_only (const EUIElement *self);
+gboolean e_ui_element_item_get_important (const EUIElement *self);
+const gchar * e_ui_element_item_get_css_classes
+ (const EUIElement *self);
+const gchar * e_ui_element_item_get_action (const EUIElement *self);
+const gchar * e_ui_element_item_get_group (const EUIElement *self);
+
+/* parser functions */
+EUIParser * e_ui_parser_new (void);
+gboolean e_ui_parser_merge_file (EUIParser *self,
+ const gchar *filename,
+ GError **error);
+gboolean e_ui_parser_merge_data (EUIParser *self,
+ const gchar *data,
+ gssize data_len,
+ GError **error);
+void e_ui_parser_clear (EUIParser *self);
+EUIElement * e_ui_parser_get_root (EUIParser *self);
+gchar * e_ui_parser_export (EUIParser *self,
+ EUIParserExportFlags flags);
+
+G_END_DECLS
+
+#endif /* E_UI_PARSER_H */
diff --git a/src/e-util/e-util-enums.h b/src/e-util/e-util-enums.h
index 180d062db8..87531e35c7 100644
--- a/src/e-util/e-util-enums.h
+++ b/src/e-util/e-util-enums.h
@@ -720,6 +720,53 @@ typedef enum {
E_HTML_LINK_TO_TEXT_REFERENCE_WITHOUT_LABEL = 3
} EHTMLLinkToText;
+/**
+ * EUIElementKind:
+ * @E_UI_ELEMENT_KIND_UNKNOWN: the kind is not known; used to indicate an error
+ * @E_UI_ELEMENT_KIND_ROOT: a root, aka toplevel, element; it contains elements for headerbar, toolbar and menu usually
+ * @E_UI_ELEMENT_KIND_HEADERBAR: a headerbar element
+ * @E_UI_ELEMENT_KIND_TOOLBAR: a toolbar element
+ * @E_UI_ELEMENT_KIND_MENU: a menu element
+ * @E_UI_ELEMENT_KIND_SUBMENU: a submenu of a menu element
+ * @E_UI_ELEMENT_KIND_PLACEHOLDER: a placeholder, into which can be added other elements
+ * @E_UI_ELEMENT_KIND_SEPARATOR: a separator element
+ * @E_UI_ELEMENT_KIND_START: a list of items to be packed at the start of a headerbar
+ * @E_UI_ELEMENT_KIND_END: a list of items to be packed at the end of a headerbar
+ * @E_UI_ELEMENT_KIND_ITEM: an item
+ *
+ * The UI element kinds. Depending on the kind, only respective functions can be
+ * called for the element.
+ *
+ * Since: 3.56
+ **/
+typedef enum _EUIElementKind {
+ E_UI_ELEMENT_KIND_UNKNOWN,
+ E_UI_ELEMENT_KIND_ROOT,
+ E_UI_ELEMENT_KIND_HEADERBAR,
+ E_UI_ELEMENT_KIND_TOOLBAR,
+ E_UI_ELEMENT_KIND_MENU,
+ E_UI_ELEMENT_KIND_SUBMENU,
+ E_UI_ELEMENT_KIND_PLACEHOLDER,
+ E_UI_ELEMENT_KIND_SEPARATOR,
+ E_UI_ELEMENT_KIND_START,
+ E_UI_ELEMENT_KIND_END,
+ E_UI_ELEMENT_KIND_ITEM
+} EUIElementKind;
+
+/**
+ * EUIParserExportFlags:
+ * @E_UI_PARSER_EXPORT_FLAG_NONE: no flag set
+ * @E_UI_PARSER_EXPORT_FLAG_INDENT: indent the output; when not set a single line of text is exported
+ *
+ * Set of flags to influence output of the e_ui_parser_export().
+ *
+ * Since: 3.56
+ **/
+typedef enum _EUIParserExportFlags { /*< flags >*/
+ E_UI_PARSER_EXPORT_FLAG_NONE = 0,
+ E_UI_PARSER_EXPORT_FLAG_INDENT = 1 << 0
+} EUIParserExportFlags;
+
G_END_DECLS
#endif /* E_UTIL_ENUMS_H */
diff --git a/src/e-util/e-util.h b/src/e-util/e-util.h
index abf7c1db3d..7b577ede71 100644
--- a/src/e-util/e-util.h
+++ b/src/e-util/e-util.h
@@ -100,10 +100,8 @@
#include
#include
#include
-#include
#include
#include
-#include
#include
#include
#include
@@ -158,7 +156,6 @@
#include
#include
#include
-#include
#include
#include
#include
@@ -177,7 +174,6 @@
#include
#include
#include
-#include
#include
#include
#include
@@ -267,6 +263,11 @@
#include
#include
#include
+#include
+#include
+#include
+#include
+#include
#include
#include
#include
diff --git a/src/e-util/e-web-view.c b/src/e-util/e-web-view.c
index 421760de62..8b1f73f3b0 100644
--- a/src/e-util/e-web-view.c
+++ b/src/e-util/e-web-view.c
@@ -35,9 +35,11 @@
#include "e-file-request.h"
#include "e-misc-utils.h"
#include "e-plugin-ui.h"
-#include "e-popup-action.h"
#include "e-selectable.h"
#include "e-stock-request.h"
+#include "e-ui-action.h"
+#include "e-ui-action-group.h"
+#include "e-ui-manager.h"
#include "e-web-view-jsc-utils.h"
#include "e-web-view.h"
@@ -50,16 +52,16 @@ typedef struct _ElementClickedData {
} ElementClickedData;
struct _EWebViewPrivate {
- GtkUIManager *ui_manager;
+ EUIManager *ui_manager;
gchar *selected_uri;
gchar *cursor_image_src;
GQueue highlights;
gboolean highlights_enabled;
- GtkAction *open_proxy;
- GtkAction *print_proxy;
- GtkAction *save_as_proxy;
+ EUIAction *open_proxy;
+ EUIAction *print_proxy;
+ EUIAction *save_as_proxy;
/* Lockdown Options */
gboolean disable_printing;
@@ -138,33 +140,6 @@ enum {
static guint signals[LAST_SIGNAL];
-static const gchar *ui =
-""
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-" "
-"";
-
/* Forward Declarations */
static void e_web_view_alert_sink_init (EAlertSinkInterface *iface);
static void e_web_view_selectable_init (ESelectableInterface *iface);
@@ -227,9 +202,12 @@ e_web_view_spell_settings_changed_cb (GSettings *settings,
}
static void
-action_copy_clipboard_cb (GtkAction *action,
- EWebView *web_view)
+action_copy_clipboard_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EWebView *web_view = user_data;
+
e_web_view_copy_clipboard (web_view);
}
@@ -284,17 +262,22 @@ e_web_view_search_web_get_selection_cb (GObject *source,
}
static void
-action_search_web_cb (GtkAction *action,
- EWebView *web_view)
+action_search_web_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EWebView *web_view = user_data;
+
e_web_view_jsc_get_selection (WEBKIT_WEB_VIEW (web_view), E_TEXT_FORMAT_PLAIN, web_view->priv->cancellable,
e_web_view_search_web_get_selection_cb, NULL);
}
static void
-action_http_open_cb (GtkAction *action,
- EWebView *web_view)
+action_http_open_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EWebView *web_view = user_data;
const gchar *uri;
gpointer parent;
@@ -351,30 +334,41 @@ webview_mailto_copy (EWebView *web_view,
}
static void
-action_mailto_copy_cb (GtkAction *action,
- EWebView *web_view)
+action_mailto_copy_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EWebView *web_view = user_data;
+
webview_mailto_copy (web_view, FALSE);
}
static void
-action_mailto_copy_raw_cb (GtkAction *action,
- EWebView *web_view)
+action_mailto_copy_raw_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EWebView *web_view = user_data;
+
webview_mailto_copy (web_view, TRUE);
}
static void
-action_select_all_cb (GtkAction *action,
- EWebView *web_view)
+action_select_all_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EWebView *web_view = user_data;
+
e_web_view_select_all (web_view);
}
static void
-action_send_message_cb (GtkAction *action,
- EWebView *web_view)
+action_send_message_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EWebView *web_view = user_data;
const gchar *uri;
gpointer parent;
gboolean handled;
@@ -393,9 +387,11 @@ action_send_message_cb (GtkAction *action,
}
static void
-action_uri_copy_cb (GtkAction *action,
- EWebView *web_view)
+action_uri_copy_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EWebView *web_view = user_data;
GtkClipboard *clipboard;
const gchar *uri;
@@ -412,123 +408,56 @@ action_uri_copy_cb (GtkAction *action,
}
static void
-action_image_copy_cb (GtkAction *action,
- EWebView *web_view)
+action_image_copy_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EWebView *web_view = user_data;
+
e_web_view_cursor_image_copy (web_view);
}
static void
-action_image_save_cb (GtkAction *action,
- EWebView *web_view)
+action_image_save_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EWebView *web_view = user_data;
+
e_web_view_cursor_image_save (web_view);
}
-static GtkActionEntry uri_entries[] = {
+static void
+action_open_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
+{
+ EWebView *web_view = user_data;
- { "uri-copy",
- "edit-copy",
- N_("_Copy Link Location"),
- "c",
- N_("Copy the link to the clipboard"),
- G_CALLBACK (action_uri_copy_cb) }
-};
-
-static GtkActionEntry http_entries[] = {
-
- { "http-open",
- "emblem-web",
- N_("_Open Link in Browser"),
- NULL,
- N_("Open the link in a web browser"),
- G_CALLBACK (action_http_open_cb) }
-};
-
-static GtkActionEntry mailto_entries[] = {
-
- { "mailto-copy",
- "edit-copy",
- N_("_Copy Name and Email Address"),
- "c",
- N_("Copy the email address with the name to the clipboard"),
- G_CALLBACK (action_mailto_copy_cb) },
-
- { "mailto-copy-raw",
- "edit-copy",
- N_("Copy Only Email Add_ress"),
- NULL,
- N_("Copy only the email address without the name to the clipboard"),
- G_CALLBACK (action_mailto_copy_raw_cb) },
-
- { "send-message",
- "mail-message-new",
- N_("_Send New Message To…"),
- NULL,
- N_("Send a mail message to this address"),
- G_CALLBACK (action_send_message_cb) }
-};
-
-static GtkActionEntry image_entries[] = {
-
- { "image-copy",
- "edit-copy",
- N_("_Copy Image"),
- "c",
- N_("Copy the image to the clipboard"),
- G_CALLBACK (action_image_copy_cb) },
-
- { "image-save",
- "document-save",
- N_("Save _Image…"),
- "s",
- N_("Save the image to a file"),
- G_CALLBACK (action_image_save_cb) }
-};
-
-static GtkActionEntry selection_entries[] = {
-
- { "copy-clipboard",
- "edit-copy",
- N_("_Copy"),
- "c",
- N_("Copy the selection"),
- G_CALLBACK (action_copy_clipboard_cb) },
-
- { "search-web",
- NULL,
- N_("Search _Web…"),
- NULL,
- N_("Search the Web with the selected text"),
- G_CALLBACK (action_search_web_cb) }
-};
-
-static GtkActionEntry standard_entries[] = {
-
- { "select-all",
- "edit-select-all",
- N_("Select _All"),
- NULL,
- N_("Select all text and images"),
- G_CALLBACK (action_select_all_cb) }
-};
+ if (web_view->priv->open_proxy)
+ g_action_activate (G_ACTION (web_view->priv->open_proxy), NULL);
+}
static void
-web_view_menu_item_select_cb (EWebView *web_view,
- GtkWidget *widget)
+action_print_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
- GtkAction *action;
- GtkActivatable *activatable;
- const gchar *tooltip;
+ EWebView *web_view = user_data;
- activatable = GTK_ACTIVATABLE (widget);
- action = gtk_activatable_get_related_action (activatable);
- tooltip = gtk_action_get_tooltip (action);
+ if (web_view->priv->print_proxy)
+ g_action_activate (G_ACTION (web_view->priv->print_proxy), NULL);
+}
- if (tooltip == NULL)
- return;
+static void
+action_save_as_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
+{
+ EWebView *web_view = user_data;
- e_web_view_status_message (web_view, tooltip);
+ if (web_view->priv->save_as_proxy)
+ g_action_activate (G_ACTION (web_view->priv->save_as_proxy), NULL);
}
static void
@@ -581,29 +510,6 @@ web_view_update_document_highlights (EWebView *web_view)
}
}
-static void
-web_view_menu_item_deselect_cb (EWebView *web_view)
-{
- e_web_view_status_message (web_view, NULL);
-}
-
-static void
-web_view_connect_proxy_cb (EWebView *web_view,
- GtkAction *action,
- GtkWidget *proxy)
-{
- if (!GTK_IS_MENU_ITEM (proxy))
- return;
-
- g_signal_connect_swapped (
- proxy, "select",
- G_CALLBACK (web_view_menu_item_select_cb), web_view);
-
- g_signal_connect_swapped (
- proxy, "deselect",
- G_CALLBACK (web_view_menu_item_deselect_cb), web_view);
-}
-
static void
web_view_got_elem_from_point_for_popup_event_cb (GObject *source_object,
GAsyncResult *result,
@@ -1931,17 +1837,32 @@ web_view_stop_loading (EWebView *web_view)
webkit_web_view_stop_loading (WEBKIT_WEB_VIEW (web_view));
}
+static void
+e_web_view_update_action_from_proxy (EUIAction *action,
+ EUIAction *proxy)
+{
+ e_ui_action_set_visible (action, proxy && e_ui_action_is_visible (proxy));
+
+ if (!e_ui_action_get_visible (action))
+ return;
+
+ e_ui_action_set_label (action, e_ui_action_get_label (proxy));
+ e_ui_action_set_icon_name (action, e_ui_action_get_icon_name (proxy));
+ e_ui_action_set_tooltip (action, e_ui_action_get_tooltip (proxy));
+ e_ui_action_set_sensitive (action, g_action_get_enabled (G_ACTION (proxy)));
+}
+
static void
web_view_update_actions (EWebView *web_view)
{
- GtkActionGroup *action_group;
+ EUIActionGroup *action_group;
+ EUIAction *action;
gboolean can_copy;
gboolean scheme_is_http = FALSE;
gboolean scheme_is_mailto = FALSE;
gboolean uri_is_valid = FALSE;
gboolean visible;
const gchar *cursor_image_src;
- const gchar *group_name;
const gchar *uri;
g_return_if_fail (E_IS_WEB_VIEW (web_view));
@@ -1967,20 +1888,17 @@ web_view_update_actions (EWebView *web_view)
}
/* Allow copying the URI even if it's malformed. */
- group_name = "uri";
visible = (uri != NULL) && !scheme_is_mailto;
- action_group = e_web_view_get_action_group (web_view, group_name);
- gtk_action_group_set_visible (action_group, visible);
+ action_group = e_web_view_get_action_group (web_view, "uri");
+ e_ui_action_group_set_visible (action_group, visible);
- group_name = "http";
visible = uri_is_valid && scheme_is_http;
- action_group = e_web_view_get_action_group (web_view, group_name);
- gtk_action_group_set_visible (action_group, visible);
+ action_group = e_web_view_get_action_group (web_view, "http");
+ e_ui_action_group_set_visible (action_group, visible);
- group_name = "mailto";
visible = uri_is_valid && scheme_is_mailto;
- action_group = e_web_view_get_action_group (web_view, group_name);
- gtk_action_group_set_visible (action_group, visible);
+ action_group = e_web_view_get_action_group (web_view, "mailto");
+ e_ui_action_group_set_visible (action_group, visible);
if (visible) {
CamelURL *curl;
@@ -1989,13 +1907,12 @@ web_view_update_actions (EWebView *web_view)
if (curl) {
CamelInternetAddress *inet_addr;
const gchar *name = NULL, *email = NULL;
- GtkAction *action;
inet_addr = camel_internet_address_new ();
camel_address_decode (CAMEL_ADDRESS (inet_addr), curl->path);
- action = gtk_action_group_get_action (action_group, "mailto-copy-raw");
- gtk_action_set_visible (action,
+ action = e_ui_action_group_get_action (action_group, "mailto-copy-raw");
+ e_ui_action_set_visible (action,
camel_internet_address_get (inet_addr, 0, &name, &email) &&
name && *name && email && *email);
@@ -2004,30 +1921,29 @@ web_view_update_actions (EWebView *web_view)
}
}
- group_name = "image";
visible = (cursor_image_src != NULL);
- action_group = e_web_view_get_action_group (web_view, group_name);
- gtk_action_group_set_visible (action_group, visible);
+ action_group = e_web_view_get_action_group (web_view, "image");
+ e_ui_action_group_set_visible (action_group, visible);
- group_name = "selection";
visible = can_copy;
- action_group = e_web_view_get_action_group (web_view, group_name);
- gtk_action_group_set_visible (action_group, visible);
+ action_group = e_web_view_get_action_group (web_view, "selection");
+ e_ui_action_group_set_visible (action_group, visible);
- group_name = "standard";
visible = (uri == NULL);
- action_group = e_web_view_get_action_group (web_view, group_name);
- gtk_action_group_set_visible (action_group, visible);
+ action_group = e_web_view_get_action_group (web_view, "standard");
+ e_ui_action_group_set_visible (action_group, visible);
- group_name = "lockdown-printing";
visible = (uri == NULL) && !web_view->priv->disable_printing;
- action_group = e_web_view_get_action_group (web_view, group_name);
- gtk_action_group_set_visible (action_group, visible);
+ action_group = e_web_view_get_action_group (web_view, "lockdown-printing");
+ e_ui_action_group_set_visible (action_group, visible);
- group_name = "lockdown-save-to-disk";
visible = (uri == NULL) && !web_view->priv->disable_save_to_disk;
- action_group = e_web_view_get_action_group (web_view, group_name);
- gtk_action_group_set_visible (action_group, visible);
+ action_group = e_web_view_get_action_group (web_view, "lockdown-save-to-disk");
+ e_ui_action_group_set_visible (action_group, visible);
+
+ e_web_view_update_action_from_proxy (e_web_view_get_action (web_view, "open"), web_view->priv->open_proxy);
+ e_web_view_update_action_from_proxy (e_web_view_get_action (web_view, "print"), web_view->priv->print_proxy);
+ e_web_view_update_action_from_proxy (e_web_view_get_action (web_view, "save-as"), web_view->priv->save_as_proxy);
}
static void
@@ -2137,14 +2053,14 @@ web_view_submit_alert (EAlertSink *alert_sink,
static void
web_view_can_execute_editing_command_cb (WebKitWebView *webkit_web_view,
GAsyncResult *result,
- GtkAction *action)
+ EUIAction *action)
{
gboolean can_do_command;
can_do_command = webkit_web_view_can_execute_editing_command_finish (
webkit_web_view, result, NULL);
- gtk_action_set_sensitive (action, can_do_command);
+ e_ui_action_set_sensitive (action, can_do_command);
g_object_unref (action);
}
@@ -2155,7 +2071,7 @@ web_view_selectable_update_actions (ESelectable *selectable,
gint n_clipboard_targets)
{
EWebView *web_view;
- GtkAction *action;
+ EUIAction *action;
gboolean can_copy = FALSE;
web_view = E_WEB_VIEW (selectable);
@@ -2163,8 +2079,8 @@ web_view_selectable_update_actions (ESelectable *selectable,
can_copy = e_web_view_has_selection (web_view);
action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
- gtk_action_set_sensitive (action, can_copy);
- gtk_action_set_tooltip (action, _("Copy the selection"));
+ e_ui_action_set_sensitive (action, can_copy);
+ e_ui_action_set_tooltip (action, _("Copy the selection"));
action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
webkit_web_view_can_execute_editing_command (
@@ -2173,7 +2089,7 @@ web_view_selectable_update_actions (ESelectable *selectable,
NULL, /* cancellable */
(GAsyncReadyCallback) web_view_can_execute_editing_command_cb,
g_object_ref (action));
- gtk_action_set_tooltip (action, _("Cut the selection"));
+ e_ui_action_set_tooltip (action, _("Cut the selection"));
action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
webkit_web_view_can_execute_editing_command (
@@ -2182,11 +2098,11 @@ web_view_selectable_update_actions (ESelectable *selectable,
NULL, /* cancellable */
(GAsyncReadyCallback) web_view_can_execute_editing_command_cb,
g_object_ref (action));
- gtk_action_set_tooltip (action, _("Paste the clipboard"));
+ e_ui_action_set_tooltip (action, _("Paste the clipboard"));
action = e_focus_tracker_get_select_all_action (focus_tracker);
- gtk_action_set_sensitive (action, TRUE);
- gtk_action_set_tooltip (action, _("Select all text and images"));
+ e_ui_action_set_sensitive (action, TRUE);
+ e_ui_action_set_tooltip (action, _("Select all text and images"));
}
static void
@@ -2399,7 +2315,7 @@ e_web_view_class_init (EWebViewClass *class)
"open-proxy",
"Open Proxy",
NULL,
- GTK_TYPE_ACTION,
+ E_TYPE_UI_ACTION,
G_PARAM_READWRITE));
g_object_class_install_property (
@@ -2409,7 +2325,7 @@ e_web_view_class_init (EWebViewClass *class)
"print-proxy",
"Print Proxy",
NULL,
- GTK_TYPE_ACTION,
+ E_TYPE_UI_ACTION,
G_PARAM_READWRITE));
g_object_class_install_property (
@@ -2419,7 +2335,7 @@ e_web_view_class_init (EWebViewClass *class)
"save-as-proxy",
"Save As Proxy",
NULL,
- GTK_TYPE_ACTION,
+ E_TYPE_UI_ACTION,
G_PARAM_READWRITE));
g_object_class_install_property (
@@ -2553,14 +2469,151 @@ e_web_view_selectable_init (ESelectableInterface *iface)
static void
e_web_view_init (EWebView *web_view)
{
- GtkUIManager *ui_manager;
- GtkActionGroup *action_group;
- EPopupAction *popup_action;
+ static const gchar *eui =
+ ""
+ ""
+ "
"
+ " "
+ ""
+ ""
+ " "
+ " "
+ " "
+ " "
+ " "
+ ""
+ ""
+ " "
+ " "
+ " "
+ " "
+ " "
+ ""
+ ""
+ ""
+ " "
+ ""
+ ""
+ "";
+
+ static const EUIActionEntry uri_entries[] = {
+
+ { "uri-copy",
+ "edit-copy",
+ N_("_Copy Link Location"),
+ "c",
+ N_("Copy the link to the clipboard"),
+ action_uri_copy_cb, NULL, NULL, NULL }
+ };
+
+ static const EUIActionEntry http_entries[] = {
+
+ { "http-open",
+ "emblem-web",
+ N_("_Open Link in Browser"),
+ NULL,
+ N_("Open the link in a web browser"),
+ action_http_open_cb, NULL, NULL, NULL }
+ };
+
+ static const EUIActionEntry mailto_entries[] = {
+
+ { "mailto-copy",
+ "edit-copy",
+ N_("_Copy Name and Email Address"),
+ "c",
+ N_("Copy the email address with the name to the clipboard"),
+ action_mailto_copy_cb, NULL, NULL, NULL },
+
+ { "mailto-copy-raw",
+ "edit-copy",
+ N_("Copy Only Email Add_ress"),
+ NULL,
+ N_("Copy only the email address without the name to the clipboard"),
+ action_mailto_copy_raw_cb, NULL, NULL, NULL },
+
+ { "send-message",
+ "mail-message-new",
+ N_("_Send New Message To…"),
+ NULL,
+ N_("Send a mail message to this address"),
+ action_send_message_cb, NULL, NULL, NULL }
+ };
+
+ static const EUIActionEntry image_entries[] = {
+
+ { "image-copy",
+ "edit-copy",
+ N_("_Copy Image"),
+ "c",
+ N_("Copy the image to the clipboard"),
+ action_image_copy_cb, NULL, NULL, NULL },
+
+ { "image-save",
+ "document-save",
+ N_("Save _Image…"),
+ "s",
+ N_("Save the image to a file"),
+ action_image_save_cb, NULL, NULL, NULL }
+ };
+
+ static const EUIActionEntry selection_entries[] = {
+
+ { "copy-clipboard",
+ "edit-copy",
+ N_("_Copy"),
+ "c",
+ N_("Copy the selection"),
+ action_copy_clipboard_cb, NULL, NULL, NULL },
+
+ { "search-web",
+ NULL,
+ N_("Search _Web…"),
+ NULL,
+ N_("Search the Web with the selected text"),
+ action_search_web_cb, NULL, NULL, NULL }
+ };
+
+ static const EUIActionEntry standard_entries[] = {
+
+ { "open",
+ NULL,
+ N_("_Open"),
+ NULL,
+ NULL,
+ action_open_cb, NULL, NULL, NULL },
+
+ { "select-all",
+ "edit-select-all",
+ N_("Select _All"),
+ NULL,
+ N_("Select all text and images"),
+ action_select_all_cb, NULL, NULL, NULL }
+ };
+
+ static const EUIActionEntry lockdown_printing_entries[] = {
+
+ { "print",
+ "document-print",
+ N_("_Print"),
+ NULL,
+ NULL,
+ action_print_cb, NULL, NULL, NULL }
+ };
+
+ static const EUIActionEntry lockdown_save_to_disk_entries[] = {
+
+ { "save-as",
+ "document-save-as",
+ N_("Save _as…"),
+ NULL,
+ NULL,
+ action_save_as_cb, NULL, NULL, NULL }
+ };
+
+ EUIManager *ui_manager;
GSettings *settings;
- const gchar *domain = GETTEXT_PACKAGE;
- const gchar *id;
gulong handler_id;
- GError *error = NULL;
web_view->priv = e_web_view_get_instance_private (web_view);
@@ -2593,13 +2646,9 @@ e_web_view_init (EWebView *web_view)
web_view, "state-flags-changed",
G_CALLBACK (style_updated_cb), NULL);
- ui_manager = gtk_ui_manager_new ();
+ ui_manager = e_ui_manager_new ();
web_view->priv->ui_manager = ui_manager;
- g_signal_connect_swapped (
- ui_manager, "connect-proxy",
- G_CALLBACK (web_view_connect_proxy_cb), web_view);
-
settings = e_util_ref_settings ("org.gnome.desktop.interface");
web_view->priv->font_settings = g_object_ref (settings);
handler_id = g_signal_connect_swapped (
@@ -2612,116 +2661,30 @@ e_web_view_init (EWebView *web_view)
web_view->priv->monospace_font_name_changed_handler_id = handler_id;
g_object_unref (settings);
- action_group = gtk_action_group_new ("uri");
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
- g_object_unref (action_group);
-
- gtk_action_group_add_actions (
- action_group, uri_entries,
- G_N_ELEMENTS (uri_entries), web_view);
-
- action_group = gtk_action_group_new ("http");
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
- g_object_unref (action_group);
-
- gtk_action_group_add_actions (
- action_group, http_entries,
- G_N_ELEMENTS (http_entries), web_view);
-
- action_group = gtk_action_group_new ("mailto");
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
- g_object_unref (action_group);
-
- gtk_action_group_add_actions (
- action_group, mailto_entries,
- G_N_ELEMENTS (mailto_entries), web_view);
-
- action_group = gtk_action_group_new ("image");
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
- g_object_unref (action_group);
-
- gtk_action_group_add_actions (
- action_group, image_entries,
- G_N_ELEMENTS (image_entries), web_view);
-
- action_group = gtk_action_group_new ("selection");
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
- g_object_unref (action_group);
-
- gtk_action_group_add_actions (
- action_group, selection_entries,
- G_N_ELEMENTS (selection_entries), web_view);
-
- action_group = gtk_action_group_new ("standard");
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
- g_object_unref (action_group);
-
- gtk_action_group_add_actions (
- action_group, standard_entries,
- G_N_ELEMENTS (standard_entries), web_view);
-
- popup_action = e_popup_action_new ("open");
- gtk_action_group_add_action (action_group, GTK_ACTION (popup_action));
- g_object_unref (popup_action);
-
- e_binding_bind_property (
- web_view, "open-proxy",
- popup_action, "related-action",
- G_BINDING_BIDIRECTIONAL |
- G_BINDING_SYNC_CREATE);
-
+ e_ui_manager_add_actions (ui_manager, "uri", NULL,
+ uri_entries, G_N_ELEMENTS (uri_entries), web_view);
+ e_ui_manager_add_actions (ui_manager, "http", NULL,
+ http_entries, G_N_ELEMENTS (http_entries), web_view);
+ e_ui_manager_add_actions (ui_manager, "mailto", NULL,
+ mailto_entries, G_N_ELEMENTS (mailto_entries), web_view);
+ e_ui_manager_add_actions (ui_manager, "image", NULL,
+ image_entries, G_N_ELEMENTS (image_entries), web_view);
+ e_ui_manager_add_actions (ui_manager, "selection", NULL,
+ selection_entries, G_N_ELEMENTS (selection_entries), web_view);
/* Support lockdown. */
-
- action_group = gtk_action_group_new ("lockdown-printing");
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
- g_object_unref (action_group);
-
- popup_action = e_popup_action_new ("print");
- gtk_action_group_add_action (action_group, GTK_ACTION (popup_action));
- g_object_unref (popup_action);
-
- e_binding_bind_property (
- web_view, "print-proxy",
- popup_action, "related-action",
- G_BINDING_BIDIRECTIONAL |
- G_BINDING_SYNC_CREATE);
-
- action_group = gtk_action_group_new ("lockdown-save-to-disk");
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
- g_object_unref (action_group);
-
- popup_action = e_popup_action_new ("save-as");
- gtk_action_group_add_action (action_group, GTK_ACTION (popup_action));
- g_object_unref (popup_action);
-
- e_binding_bind_property (
- web_view, "save-as-proxy",
- popup_action, "related-action",
- G_BINDING_BIDIRECTIONAL |
- G_BINDING_SYNC_CREATE);
-
- /* Because we are loading from a hard-coded string, there is
- * no chance of I/O errors. Failure here implies a malformed
- * UI definition. Full stop. */
- gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
- if (error != NULL)
- g_error ("%s", error->message);
-
- id = "org.gnome.evolution.webview";
- e_plugin_ui_register_manager (ui_manager, id, web_view);
- e_plugin_ui_enable_manager (ui_manager, id);
+ e_ui_manager_add_actions (ui_manager, "lockdown-printing", NULL,
+ lockdown_printing_entries, G_N_ELEMENTS (lockdown_printing_entries), web_view);
+ e_ui_manager_add_actions (ui_manager, "lockdown-save-to-disk", NULL,
+ lockdown_save_to_disk_entries, G_N_ELEMENTS (lockdown_save_to_disk_entries), web_view);
+ /* finally add actions with the .eui data */
+ e_ui_manager_add_actions_with_eui_data (ui_manager, "standard", NULL,
+ standard_entries, G_N_ELEMENTS (standard_entries), web_view, eui);
web_view->priv->element_clicked_cbs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_ptr_array_unref);
web_view->priv->cancellable = NULL;
+
+ e_ui_manager_set_action_groups_widget (ui_manager, GTK_WIDGET (web_view));
}
GtkWidget *
@@ -2982,7 +2945,7 @@ e_web_view_set_cursor_image_src (EWebView *web_view,
g_object_notify (G_OBJECT (web_view), "cursor-image-src");
}
-GtkAction *
+EUIAction *
e_web_view_get_open_proxy (EWebView *web_view)
{
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
@@ -2992,7 +2955,7 @@ e_web_view_get_open_proxy (EWebView *web_view)
void
e_web_view_set_open_proxy (EWebView *web_view,
- GtkAction *open_proxy)
+ EUIAction *open_proxy)
{
g_return_if_fail (E_IS_WEB_VIEW (web_view));
@@ -3000,7 +2963,7 @@ e_web_view_set_open_proxy (EWebView *web_view,
return;
if (open_proxy != NULL) {
- g_return_if_fail (GTK_IS_ACTION (open_proxy));
+ g_return_if_fail (E_IS_UI_ACTION (open_proxy));
g_object_ref (open_proxy);
}
@@ -3023,7 +2986,7 @@ e_web_view_get_paste_target_list (EWebView *web_view)
return NULL;
}
-GtkAction *
+EUIAction *
e_web_view_get_print_proxy (EWebView *web_view)
{
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
@@ -3033,7 +2996,7 @@ e_web_view_get_print_proxy (EWebView *web_view)
void
e_web_view_set_print_proxy (EWebView *web_view,
- GtkAction *print_proxy)
+ EUIAction *print_proxy)
{
g_return_if_fail (E_IS_WEB_VIEW (web_view));
@@ -3041,7 +3004,7 @@ e_web_view_set_print_proxy (EWebView *web_view,
return;
if (print_proxy != NULL) {
- g_return_if_fail (GTK_IS_ACTION (print_proxy));
+ g_return_if_fail (E_IS_UI_ACTION (print_proxy));
g_object_ref (print_proxy);
}
@@ -3053,7 +3016,7 @@ e_web_view_set_print_proxy (EWebView *web_view,
g_object_notify (G_OBJECT (web_view), "print-proxy");
}
-GtkAction *
+EUIAction *
e_web_view_get_save_as_proxy (EWebView *web_view)
{
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
@@ -3063,7 +3026,7 @@ e_web_view_get_save_as_proxy (EWebView *web_view)
void
e_web_view_set_save_as_proxy (EWebView *web_view,
- GtkAction *save_as_proxy)
+ EUIAction *save_as_proxy)
{
g_return_if_fail (E_IS_WEB_VIEW (web_view));
@@ -3071,7 +3034,7 @@ e_web_view_set_save_as_proxy (EWebView *web_view,
return;
if (save_as_proxy != NULL) {
- g_return_if_fail (GTK_IS_ACTION (save_as_proxy));
+ g_return_if_fail (E_IS_UI_ACTION (save_as_proxy));
g_object_ref (save_as_proxy);
}
@@ -3151,32 +3114,32 @@ e_web_view_disable_highlights (EWebView *web_view)
web_view->priv->highlights_enabled = FALSE;
}
-GtkAction *
+EUIAction *
e_web_view_get_action (EWebView *web_view,
const gchar *action_name)
{
- GtkUIManager *ui_manager;
+ EUIManager *ui_manager;
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
g_return_val_if_fail (action_name != NULL, NULL);
ui_manager = e_web_view_get_ui_manager (web_view);
- return e_lookup_action (ui_manager, action_name);
+ return e_ui_manager_get_action (ui_manager, action_name);
}
-GtkActionGroup *
+EUIActionGroup *
e_web_view_get_action_group (EWebView *web_view,
const gchar *group_name)
{
- GtkUIManager *ui_manager;
+ EUIManager *ui_manager;
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
g_return_val_if_fail (group_name != NULL, NULL);
ui_manager = e_web_view_get_ui_manager (web_view);
- return e_lookup_action_group (ui_manager, group_name);
+ return e_ui_manager_get_action_group (ui_manager, group_name);
}
void
@@ -3291,7 +3254,7 @@ e_web_view_zoom_out (EWebView *web_view)
webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (web_view), zoom_level);
}
-GtkUIManager *
+EUIManager *
e_web_view_get_ui_manager (EWebView *web_view)
{
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
@@ -3299,54 +3262,28 @@ e_web_view_get_ui_manager (EWebView *web_view)
return web_view->priv->ui_manager;
}
-static void
-e_web_view_popup_menu_deactivate_cb (GtkMenu *popup_menu,
- GtkWidget *web_view)
-{
- g_return_if_fail (GTK_IS_MENU (popup_menu));
-
- g_signal_handlers_disconnect_by_func (popup_menu, e_web_view_popup_menu_deactivate_cb, web_view);
- gtk_menu_detach (popup_menu);
-}
-
-GtkWidget *
-e_web_view_get_popup_menu (EWebView *web_view)
-{
- GtkUIManager *ui_manager;
- GtkWidget *menu;
-
- g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
-
- ui_manager = e_web_view_get_ui_manager (web_view);
- menu = gtk_ui_manager_get_widget (ui_manager, "/context");
- g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
-
- g_warn_if_fail (!gtk_menu_get_attach_widget (GTK_MENU (menu)));
-
- gtk_menu_attach_to_widget (GTK_MENU (menu),
- GTK_WIDGET (web_view),
- NULL);
-
- g_signal_connect (
- menu, "deactivate",
- G_CALLBACK (e_web_view_popup_menu_deactivate_cb), web_view);
-
- return menu;
-}
-
void
e_web_view_show_popup_menu (EWebView *web_view,
GdkEvent *event)
{
- GtkWidget *menu;
+ EUIManager *ui_manager;
+ GtkMenu *menu;
+ GObject *ui_item;
g_return_if_fail (E_IS_WEB_VIEW (web_view));
e_web_view_update_actions (web_view);
- menu = e_web_view_get_popup_menu (web_view);
+ ui_manager = e_web_view_get_ui_manager (web_view);
+ ui_item = e_ui_manager_create_item (ui_manager, "context");
+ menu = GTK_MENU (gtk_menu_new_from_model (G_MENU_MODEL (ui_item)));
+ g_clear_object (&ui_item);
- gtk_menu_popup_at_pointer (GTK_MENU (menu), event);
+ gtk_menu_attach_to_widget (menu, GTK_WIDGET (web_view), NULL);
+
+ e_util_connect_menu_detach_after_deactivate (menu);
+
+ gtk_menu_popup_at_pointer (menu, event);
}
/**
diff --git a/src/e-util/e-web-view.h b/src/e-util/e-web-view.h
index ef238d8f68..5f2461d159 100644
--- a/src/e-util/e-web-view.h
+++ b/src/e-util/e-web-view.h
@@ -31,6 +31,9 @@
#include
#include
#include
+#include
+#include
+#include
/* Standard GObject macros */
#define E_TYPE_WEB_VIEW \
@@ -181,17 +184,17 @@ void e_web_view_set_selected_uri (EWebView *web_view,
const gchar * e_web_view_get_cursor_image_src (EWebView *web_view);
void e_web_view_set_cursor_image_src (EWebView *web_view,
const gchar *src_uri);
-GtkAction * e_web_view_get_open_proxy (EWebView *web_view);
+EUIAction * e_web_view_get_open_proxy (EWebView *web_view);
void e_web_view_set_open_proxy (EWebView *web_view,
- GtkAction *open_proxy);
+ EUIAction *open_proxy);
GtkTargetList * e_web_view_get_paste_target_list
(EWebView *web_view);
-GtkAction * e_web_view_get_print_proxy (EWebView *web_view);
+EUIAction * e_web_view_get_print_proxy (EWebView *web_view);
void e_web_view_set_print_proxy (EWebView *web_view,
- GtkAction *print_proxy);
-GtkAction * e_web_view_get_save_as_proxy (EWebView *web_view);
+ EUIAction *print_proxy);
+EUIAction * e_web_view_get_save_as_proxy (EWebView *web_view);
void e_web_view_set_save_as_proxy (EWebView *web_view,
- GtkAction *save_as_proxy);
+ EUIAction *save_as_proxy);
void e_web_view_get_last_popup_place (EWebView *web_view,
gchar **out_iframe_src,
gchar **out_iframe_id,
@@ -202,9 +205,9 @@ void e_web_view_add_highlight (EWebView *web_view,
void e_web_view_clear_highlights (EWebView *web_view);
void e_web_view_update_highlights (EWebView *web_view);
void e_web_view_disable_highlights (EWebView *web_view);
-GtkAction * e_web_view_get_action (EWebView *web_view,
+EUIAction * e_web_view_get_action (EWebView *web_view,
const gchar *action_name);
-GtkActionGroup *e_web_view_get_action_group (EWebView *web_view,
+EUIActionGroup *e_web_view_get_action_group (EWebView *web_view,
const gchar *group_name);
void e_web_view_copy_clipboard (EWebView *web_view);
void e_web_view_cut_clipboard (EWebView *web_view);
@@ -217,8 +220,7 @@ void e_web_view_unselect_all (EWebView *web_view);
void e_web_view_zoom_100 (EWebView *web_view);
void e_web_view_zoom_in (EWebView *web_view);
void e_web_view_zoom_out (EWebView *web_view);
-GtkUIManager * e_web_view_get_ui_manager (EWebView *web_view);
-GtkWidget * e_web_view_get_popup_menu (EWebView *web_view);
+EUIManager * e_web_view_get_ui_manager (EWebView *web_view);
void e_web_view_show_popup_menu (EWebView *web_view,
GdkEvent *event);
EActivity * e_web_view_new_activity (EWebView *web_view);
diff --git a/src/e-util/test-html-editor-units-utils.c b/src/e-util/test-html-editor-units-utils.c
index 0ed746be93..edab71dd5c 100644
--- a/src/e-util/test-html-editor-units-utils.c
+++ b/src/e-util/test-html-editor-units-utils.c
@@ -741,11 +741,15 @@ test_utils_html_equal (TestFixture *fixture,
script = e_web_view_jsc_printf_script (
"var EvoEditorTest = {};\n"
- "EvoEditorTest.fixupBlockquotes = function(parent) {\n"
+ "EvoEditorTest.fixupElems = function(parent) {\n"
" var ii, elems = parent.getElementsByTagName(\"BLOCKQUOTE\");\n"
" for (ii = 0; ii < elems.length; ii++) {\n"
" elems[ii].removeAttribute(\"spellcheck\");\n"
" }\n"
+ " elems = parent.getElementsByTagName(\"STYLE\");\n"
+ " for (ii = elems.length; ii--;) {\n"
+ " elems[ii].remove();\n"
+ " }\n"
"}\n"
"EvoEditorTest.isHTMLEqual = function(html1, html2) {\n"
" var elem1, elem2;\n"
@@ -753,8 +757,8 @@ test_utils_html_equal (TestFixture *fixture,
" elem2 = document.createElement(\"testHtmlEqual\");\n"
" elem1.innerHTML = html1.replace(/ /g, \" \").replace(/ /g, \" \");\n"
" elem2.innerHTML = html2.replace(/ /g, \" \").replace(/ /g, \" \");\n"
- " EvoEditorTest.fixupBlockquotes(elem1);\n"
- " EvoEditorTest.fixupBlockquotes(elem2);\n"
+ " EvoEditorTest.fixupElems(elem1);\n"
+ " EvoEditorTest.fixupElems(elem2);\n"
" elem1.normalize();\n"
" elem2.normalize();\n"
" return elem1.isEqualNode(elem2);\n"
@@ -929,7 +933,7 @@ static gboolean
test_utils_execute_action (TestFixture *fixture,
const gchar *action_name)
{
- GtkAction *action;
+ EUIAction *action;
g_return_val_if_fail (fixture != NULL, FALSE);
g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE);
@@ -937,7 +941,11 @@ test_utils_execute_action (TestFixture *fixture,
action = e_html_editor_get_action (fixture->editor, action_name);
if (action) {
- gtk_action_activate (action);
+ GVariant *target;
+
+ target = e_ui_action_ref_target (action);
+ g_action_activate (G_ACTION (action), target);
+ g_clear_pointer (&target, g_variant_unref);
} else {
g_warning ("%s: Failed to find action '%s'", G_STRFUNC, action_name);
return FALSE;
diff --git a/src/e-util/test-html-editor-units.c b/src/e-util/test-html-editor-units.c
index cb31f7bb88..7739677fc9 100644
--- a/src/e-util/test-html-editor-units.c
+++ b/src/e-util/test-html-editor-units.c
@@ -3522,7 +3522,9 @@ test_link_insert_dialog (TestFixture *fixture)
"type:a link example: \n"
"action:insert-link\n"
"type:http://www.gnome.org\n"
- "seq:n\n",
+ "seq:A\n" /* Alt+A to press the "Add" button in the popover */
+ "type:a\n"
+ "seq:a\n",
HTML_PREFIX "" HTML_SUFFIX,
"a link example: http://www.gnome.org\n"))
g_test_fail ();
@@ -3531,13 +3533,17 @@ test_link_insert_dialog (TestFixture *fixture)
static void
test_link_insert_dialog_selection (TestFixture *fixture)
{
+ test_utils_fixture_change_setting_string (fixture, "org.gnome.evolution.mail", "html-link-to-text", "none");
+
if (!test_utils_run_simple_test (fixture,
"mode:html\n"
"type:a link example: GNOME\n"
"seq:CSlsc\n"
"action:insert-link\n"
"type:http://www.gnome.org\n"
- "seq:n\n",
+ "seq:A\n" /* Alt+A to press the "Add" button in the popover */
+ "type:a\n"
+ "seq:a\n",
HTML_PREFIX "" HTML_SUFFIX,
"a link example: GNOME\n"))
g_test_fail ();
@@ -3557,6 +3563,8 @@ test_link_insert_typed (TestFixture *fixture)
static void
test_link_insert_typed_change_description (TestFixture *fixture)
{
+ test_utils_fixture_change_setting_string (fixture, "org.gnome.evolution.mail", "html-link-to-text", "none");
+
if (!test_utils_run_simple_test (fixture,
"mode:html\n"
"type:www.gnome.org \n"
@@ -3566,7 +3574,9 @@ test_link_insert_typed_change_description (TestFixture *fixture)
"type:D\n"
"seq:a\n"
"type:GNOME\n"
- "seq:n\n",
+ "seq:A\n" /* Alt+A to press the "Update" button in the popover */
+ "type:a\n"
+ "seq:a\n",
HTML_PREFIX "" HTML_SUFFIX,
"GNOME \n"))
g_test_fail ();
@@ -3591,6 +3601,8 @@ test_link_insert_dialog_remove_link (TestFixture *fixture)
static void
test_link_insert_typed_append (TestFixture *fixture)
{
+ test_utils_fixture_change_setting_string (fixture, "org.gnome.evolution.mail", "html-link-to-text", "none");
+
if (!test_utils_run_simple_test (fixture,
"mode:html\n"
"type:www.gnome.org \n"
@@ -3604,6 +3616,8 @@ test_link_insert_typed_append (TestFixture *fixture)
static void
test_link_insert_typed_remove (TestFixture *fixture)
{
+ test_utils_fixture_change_setting_string (fixture, "org.gnome.evolution.mail", "html-link-to-text", "none");
+
if (!test_utils_run_simple_test (fixture,
"mode:html\n"
"type:www.gnome.org \n"
diff --git a/src/e-util/test-html-editor.c b/src/e-util/test-html-editor.c
index f8a9bb8cf6..6f103e9907 100644
--- a/src/e-util/test-html-editor.c
+++ b/src/e-util/test-html-editor.c
@@ -31,41 +31,6 @@
which prints a constructed message, like within the message preview. */
/* #define ENABLE_PRINT */
-static const gchar *file_ui =
-"\n"
-" \n"
-" \n"
-" \n"
-" \n"
-" \n"
-" \n"
-#ifdef ENABLE_PRINT
-" \n"
-" \n"
-" \n"
-#endif /* ENABLE_PRINT */
-" \n"
-" \n"
-" \n"
-" \n"
-" \n"
-" \n"
-"";
-
-static const gchar *view_ui =
-"\n"
-" \n"
-" \n"
-" \n"
-" \n"
-" \n"
-" \n"
-" \n"
-" \n"
-" \n"
-" \n"
-"";
-
enum {
GET_CONTENT_PLAIN,
GET_CONTENT_HTML,
@@ -291,32 +256,52 @@ view_source_dialog (EHTMLEditor *editor,
}
static void
-action_new_editor_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_new_editor_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
+ g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
create_new_editor ();
}
#ifdef ENABLE_PRINT
static void
-action_print_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_print_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
+ g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
print (editor, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG);
}
static void
-action_print_preview_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_print_preview_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
+ g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
print (editor, GTK_PRINT_OPERATION_ACTION_PREVIEW);
}
#endif /* ENABLE_PRINT */
static void
-action_quit_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_quit_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
+ g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
gtk_main_quit ();
}
@@ -333,12 +318,16 @@ html_editor_save_done_cb (GObject *source_object,
}
static void
-action_save_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_save_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
const gchar *filename;
gboolean as_html;
+ g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
if (e_html_editor_get_filename (editor) == NULL)
if (save_dialog (editor) == GTK_RESPONSE_CANCEL)
return;
@@ -350,12 +339,16 @@ action_save_cb (GtkAction *action,
}
static void
-action_save_as_cb (GtkAction *action,
- EHTMLEditor *editor)
+action_save_as_cb (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
const gchar *filename;
gboolean as_html;
+ g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
if (save_dialog (editor) == GTK_RESPONSE_CANCEL)
return;
@@ -366,49 +359,77 @@ action_save_as_cb (GtkAction *action,
}
static void
-action_toggle_editor (GtkAction *action,
- EHTMLEditor *editor)
+action_toggle_editor (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
+ g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
cnt_editor = e_html_editor_get_content_editor (editor);
e_content_editor_set_editable (cnt_editor, !e_content_editor_is_editable (cnt_editor));
}
static void
-action_view_html_output (GtkAction *action,
- EHTMLEditor *editor)
+action_view_html_output (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
+ g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
view_source_dialog (editor, _("HTML Output"), GET_CONTENT_HTML, FALSE);
}
static void
-action_view_html_source (GtkAction *action,
- EHTMLEditor *editor)
+action_view_html_source (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
+ g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
view_source_dialog (editor, _("HTML Source"), GET_CONTENT_HTML, TRUE);
}
static void
-action_view_plain_source (GtkAction *action,
- EHTMLEditor *editor)
+action_view_plain_source (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
+ g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
view_source_dialog (editor, _("Plain Source"), GET_CONTENT_PLAIN, FALSE);
}
static void
-action_view_draft_source (GtkAction *action,
- EHTMLEditor *editor)
+action_view_draft_source (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
+ EHTMLEditor *editor = user_data;
+
+ g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
view_source_dialog (editor, _("Draft"), GET_CONTENT_DRAFT, TRUE);
}
static void
-action_view_inspector (GtkAction *action,
- EHTMLEditor *editor)
+action_view_inspector (EUIAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
- WebKitWebInspector *inspector;
+ EHTMLEditor *editor = user_data;
EContentEditor *cnt_editor;
+ WebKitWebInspector *inspector;
+
+ g_return_if_fail (E_IS_HTML_EDITOR (editor));
cnt_editor = e_html_editor_get_content_editor (editor);
if (WEBKIT_IS_WEB_VIEW (cnt_editor)) {
@@ -419,111 +440,6 @@ action_view_inspector (GtkAction *action,
}
}
-static GtkActionEntry file_entries[] = {
- { "new-editor",
- "document-new",
- N_("_New editor"),
- "N",
- NULL,
- G_CALLBACK (action_new_editor_cb) },
-
-#ifdef ENABLE_PRINT
- { "print",
- "document-print",
- N_("_Print…"),
- "p",
- NULL,
- G_CALLBACK (action_print_cb) },
-
- { "print-preview",
- "document-print-preview",
- N_("Print Pre_view"),
- "p",
- NULL,
- G_CALLBACK (action_print_preview_cb) },
-#endif /* ENABLE_PRINT */
-
- { "quit",
- "application-exit",
- N_("_Quit"),
- NULL,
- NULL,
- G_CALLBACK (action_quit_cb) },
-
- { "save",
- "document-save",
- N_("_Save"),
- NULL,
- NULL,
- G_CALLBACK (action_save_cb) },
-
- { "save-as",
- "document-save-as",
- N_("Save _As…"),
- NULL,
- NULL,
- G_CALLBACK (action_save_as_cb) },
-
- { "disable-editor",
- NULL,
- N_("Disable/Enable Editor"),
- NULL,
- NULL,
- G_CALLBACK (action_toggle_editor) },
-
- { "file-menu",
- NULL,
- N_("_File"),
- NULL,
- NULL,
- NULL }
-};
-
-static GtkActionEntry view_entries[] = {
-
- { "view-html-output",
- NULL,
- N_("HTML _Output"),
- NULL,
- NULL,
- G_CALLBACK (action_view_html_output) },
-
- { "view-html-source",
- NULL,
- N_("_HTML Source"),
- NULL,
- NULL,
- G_CALLBACK (action_view_html_source) },
-
- { "view-plain-source",
- NULL,
- N_("_Plain Source"),
- NULL,
- NULL,
- G_CALLBACK (action_view_plain_source) },
-
- { "view-draft-source",
- NULL,
- N_("_Draft Source"),
- NULL,
- NULL,
- G_CALLBACK (action_view_draft_source) },
-
- { "view-webkit-inspector",
- NULL,
- N_("Inspector"),
- NULL,
- "