Cadabra
Computer algebra system for field theory problems
Loading...
Searching...
No Matches
GtkCompat.hh
Go to the documentation of this file.
1
2#pragma once
3
15
16#include <gtkmm/box.h>
17#include <gtkmm/dialog.h>
18#include <gtkmm/messagedialog.h>
19#include <gtkmm/aboutdialog.h>
20#include <gtkmm/label.h>
21#include <gtkmm/filechooser.h>
22#include <gtkmm/paned.h>
23#include <gtkmm/scrolledwindow.h>
24#include <gtkmm/window.h>
25#include <gtkmm/cssprovider.h>
26#include <gtkmm/stylecontext.h>
27#include <gdkmm/display.h>
28// display.h only forward-declares Monitor, and monitor_geometry() below calls
29// through it.
30#include <gdkmm/monitor.h>
31#include <gdkmm/rectangle.h>
32#ifdef USE_GTK4
33// Likewise Gdk::Surface, which get_monitor_at_surface() takes.
34#include <gdkmm/surface.h>
35#endif
36#include <gtkmm/textbuffer.h>
37#include <gdkmm/pixbuf.h>
38#include <giomm/menu.h>
39#include <giomm/inputstream.h>
40#ifdef USE_GTK4
41#include <gdkmm/clipboard.h>
42#include <gdkmm/contentprovider.h>
43#include <gdkmm/contentformats.h>
44#else
45#include <gtkmm/clipboard.h>
46#include <gtkmm/selectiondata.h>
47#include <gtkmm/targetentry.h>
48#endif
49#include <giomm/simpleactiongroup.h>
50#ifdef USE_GTK4
51#include <gtkmm/popovermenu.h>
52#else
53#include <gtkmm/menu.h>
54#include <gtkmm/menuitem.h>
55#endif
56#ifdef USE_GTK4
57#include <gdkmm/texture.h>
58#endif
59#ifdef USE_GTK4
60#include <gtkmm/gestureclick.h>
61#else
62#include <gtkmm/gesturemultipress.h>
63#endif
64#include <gtkmm/widget.h>
65#include <gtkmm/textview.h>
66// Gdk::ModifierType moved between headers: gdkmm/types.h in gdkmm-3,
67// gdkmm/enums.h in gdkmm-4.
68#ifdef USE_GTK4
69#include <gdkmm/enums.h>
70#else
71#include <gdkmm/types.h>
72#endif
73#include <giomm/application.h>
74#include <pangomm/attributes.h>
75#include <pangomm/tabarray.h>
76#include <glibmm/main.h>
77#include <glibmm/ustring.h>
78#include <glibmm/value.h>
79
80#include <type_traits>
81#include <memory>
82#include <functional>
83#include <string>
84#include <vector>
85
86namespace cdb {
87
91
92#ifdef USE_GTK4
93 constexpr auto APP_HANDLES_OPEN = Gio::Application::Flags::HANDLES_OPEN;
94 constexpr auto APP_HANDLES_COMMAND_LINE = Gio::Application::Flags::HANDLES_COMMAND_LINE;
95 constexpr auto APP_NON_UNIQUE = Gio::Application::Flags::NON_UNIQUE;
96
97 constexpr auto OPTION_TYPE_BOOL = Gio::Application::OptionType::BOOL;
98 constexpr auto OPTION_TYPE_INT = Gio::Application::OptionType::INT;
99 constexpr auto OPTION_TYPE_STRING = Gio::Application::OptionType::STRING;
100#else
101 constexpr auto APP_HANDLES_OPEN = Gio::APPLICATION_HANDLES_OPEN;
102 constexpr auto APP_HANDLES_COMMAND_LINE = Gio::APPLICATION_HANDLES_COMMAND_LINE;
103 constexpr auto APP_NON_UNIQUE = Gio::APPLICATION_NON_UNIQUE;
104
105 constexpr auto OPTION_TYPE_BOOL = Gio::Application::OptionType::OPTION_TYPE_BOOL;
106 constexpr auto OPTION_TYPE_INT = Gio::Application::OptionType::OPTION_TYPE_INT;
107 constexpr auto OPTION_TYPE_STRING = Gio::Application::OptionType::OPTION_TYPE_STRING;
108#endif
109
110#ifdef USE_GTK4
111 constexpr auto ORIENT_HORIZONTAL = Gtk::Orientation::HORIZONTAL;
112 constexpr auto ORIENT_VERTICAL = Gtk::Orientation::VERTICAL;
113
114 constexpr auto ALIGN_FILL = Gtk::Align::FILL;
115 constexpr auto ALIGN_START = Gtk::Align::START;
116 constexpr auto ALIGN_END = Gtk::Align::END;
117 constexpr auto ALIGN_CENTER = Gtk::Align::CENTER;
118
119 constexpr auto POLICY_ALWAYS = Gtk::PolicyType::ALWAYS;
120 constexpr auto POLICY_AUTOMATIC = Gtk::PolicyType::AUTOMATIC;
121 constexpr auto POLICY_NEVER = Gtk::PolicyType::NEVER;
122
123 constexpr auto SIZE_REQUEST_HEIGHT_FOR_WIDTH = Gtk::SizeRequestMode::HEIGHT_FOR_WIDTH;
124 constexpr auto SIZE_REQUEST_WIDTH_FOR_HEIGHT = Gtk::SizeRequestMode::WIDTH_FOR_HEIGHT;
125
126 constexpr auto TEXT_WINDOW_TEXT = Gtk::TextWindowType::TEXT;
127 constexpr auto WRAP_WORD_CHAR = Gtk::WrapMode::WORD_CHAR;
128 constexpr auto WRAP_NONE = Gtk::WrapMode::NONE;
129 constexpr auto COLORSPACE_RGB = Gdk::Colorspace::RGB;
130
131 constexpr auto STATE_FLAG_NORMAL = Gtk::StateFlags::NORMAL;
132 constexpr auto STATE_FLAG_SELECTED = Gtk::StateFlags::SELECTED;
133
134 constexpr auto RESPONSE_OK = Gtk::ResponseType::OK;
135 constexpr auto RESPONSE_CANCEL = Gtk::ResponseType::CANCEL;
136 constexpr auto RESPONSE_CLOSE = Gtk::ResponseType::CLOSE;
137 constexpr auto RESPONSE_YES = Gtk::ResponseType::YES;
138 constexpr auto RESPONSE_APPLY = Gtk::ResponseType::APPLY;
139
140 constexpr auto MESSAGE_INFO = Gtk::MessageType::INFO;
141 constexpr auto MESSAGE_WARNING = Gtk::MessageType::WARNING;
142 constexpr auto MESSAGE_QUESTION = Gtk::MessageType::QUESTION;
143 constexpr auto MESSAGE_ERROR = Gtk::MessageType::ERROR;
144
145 constexpr auto BUTTONS_NONE = Gtk::ButtonsType::NONE;
146 constexpr auto BUTTONS_OK = Gtk::ButtonsType::OK;
147 constexpr auto BUTTONS_YES_NO = Gtk::ButtonsType::YES_NO;
148
149 constexpr auto FILE_CHOOSER_ACTION_OPEN = Gtk::FileChooser::Action::OPEN;
150 constexpr auto FILE_CHOOSER_ACTION_SAVE = Gtk::FileChooser::Action::SAVE;
151 constexpr auto LICENSE_GPL_3_0 = Gtk::License::GPL_3_0;
152 // pangomm-2.48 goes with gtkmm-4, pangomm-1.4 with gtkmm-3.
153 constexpr auto PANGO_TAB_LEFT = Pango::TabAlign::LEFT;
154 constexpr auto PANGO_UNDERLINE_ERROR = Pango::Underline::ERROR;
155#else
156 constexpr auto ORIENT_HORIZONTAL = Gtk::ORIENTATION_HORIZONTAL;
157 constexpr auto ORIENT_VERTICAL = Gtk::ORIENTATION_VERTICAL;
158
159 constexpr auto ALIGN_FILL = Gtk::ALIGN_FILL;
160 constexpr auto ALIGN_START = Gtk::ALIGN_START;
161 constexpr auto ALIGN_END = Gtk::ALIGN_END;
162 constexpr auto ALIGN_CENTER = Gtk::ALIGN_CENTER;
163
164 constexpr auto POLICY_ALWAYS = Gtk::POLICY_ALWAYS;
165 constexpr auto POLICY_AUTOMATIC = Gtk::POLICY_AUTOMATIC;
166 constexpr auto POLICY_NEVER = Gtk::POLICY_NEVER;
167
168 constexpr auto SIZE_REQUEST_HEIGHT_FOR_WIDTH = Gtk::SizeRequestMode::SIZE_REQUEST_HEIGHT_FOR_WIDTH;
169 constexpr auto SIZE_REQUEST_WIDTH_FOR_HEIGHT = Gtk::SizeRequestMode::SIZE_REQUEST_WIDTH_FOR_HEIGHT;
170
171 constexpr auto TEXT_WINDOW_TEXT = Gtk::TEXT_WINDOW_TEXT;
172 constexpr auto WRAP_WORD_CHAR = Gtk::WRAP_WORD_CHAR;
173 constexpr auto WRAP_NONE = Gtk::WRAP_NONE;
174 constexpr auto COLORSPACE_RGB = Gdk::COLORSPACE_RGB;
175
176 constexpr auto STATE_FLAG_NORMAL = Gtk::STATE_FLAG_NORMAL;
177 constexpr auto STATE_FLAG_SELECTED = Gtk::STATE_FLAG_SELECTED;
178
179 constexpr auto RESPONSE_OK = Gtk::RESPONSE_OK;
180 constexpr auto RESPONSE_CANCEL = Gtk::RESPONSE_CANCEL;
181 constexpr auto RESPONSE_CLOSE = Gtk::RESPONSE_CLOSE;
182 constexpr auto RESPONSE_YES = Gtk::RESPONSE_YES;
183 constexpr auto RESPONSE_APPLY = Gtk::RESPONSE_APPLY;
184
185 constexpr auto MESSAGE_INFO = Gtk::MESSAGE_INFO;
186 constexpr auto MESSAGE_WARNING = Gtk::MESSAGE_WARNING;
187 constexpr auto MESSAGE_QUESTION = Gtk::MESSAGE_QUESTION;
188 constexpr auto MESSAGE_ERROR = Gtk::MESSAGE_ERROR;
189
190 constexpr auto BUTTONS_NONE = Gtk::BUTTONS_NONE;
191 constexpr auto BUTTONS_OK = Gtk::BUTTONS_OK;
192 constexpr auto BUTTONS_YES_NO = Gtk::BUTTONS_YES_NO;
193
194 constexpr auto FILE_CHOOSER_ACTION_OPEN = Gtk::FILE_CHOOSER_ACTION_OPEN;
195 constexpr auto FILE_CHOOSER_ACTION_SAVE = Gtk::FILE_CHOOSER_ACTION_SAVE;
196 constexpr auto LICENSE_GPL_3_0 = Gtk::License::LICENSE_GPL_3_0;
197 constexpr auto PANGO_TAB_LEFT = Pango::TAB_LEFT;
198 constexpr auto PANGO_UNDERLINE_ERROR = Pango::Underline::UNDERLINE_ERROR;
199#endif
200
203
204 inline Gtk::TextBuffer::iterator insert_image(const Glib::RefPtr<Gtk::TextBuffer>& buffer,
205 const Gtk::TextBuffer::iterator& pos,
206 const Glib::RefPtr<Gdk::Pixbuf>& pixbuf)
207 {
208#ifdef USE_GTK4
209 return buffer->insert_paintable(pos, Gdk::Texture::create_for_pixbuf(pixbuf));
210#else
211 return buffer->insert_pixbuf(pos, pixbuf);
212#endif
213 }
214
229
230 enum class Expand { no, yes };
231
232 inline void box_append(Gtk::Box& box, Gtk::Widget& child,
233 Expand expand = Expand::no, int padding = 0)
234 {
235 const bool vertical = (box.get_orientation() == ORIENT_VERTICAL);
236
237 if(expand == Expand::yes) {
238 if(vertical) child.set_vexpand(true);
239 else child.set_hexpand(true);
240 }
241 if(padding > 0) {
242 if(vertical) {
243 child.set_margin_top(padding);
244 child.set_margin_bottom(padding);
245 }
246 else {
247 child.set_margin_start(padding);
248 child.set_margin_end(padding);
249 }
250 }
251#ifdef USE_GTK4
252 box.append(child);
253#else
254 box.pack_start(child,
255 expand==Expand::yes ? Gtk::PACK_EXPAND_WIDGET : Gtk::PACK_SHRINK,
256 0);
257 child.show();
258#endif
259 }
260
263
264#ifdef USE_GTK4
265 constexpr auto MODIFIER_SHIFT = Gdk::ModifierType::SHIFT_MASK;
266 constexpr auto MODIFIER_CONTROL = Gdk::ModifierType::CONTROL_MASK;
267#else
268 constexpr auto MODIFIER_SHIFT = Gdk::SHIFT_MASK;
269 constexpr auto MODIFIER_CONTROL = Gdk::CONTROL_MASK;
270#endif
271
272 inline bool has_modifier(Gdk::ModifierType state, Gdk::ModifierType modifier)
273 {
274 return (state & modifier) == modifier;
275 }
276
280
281#ifdef USE_GTK4
282 using ClickGesture = Gtk::GestureClick;
283 constexpr auto SEQ_CLAIMED = Gtk::EventSequenceState::CLAIMED;
284#else
285 using ClickGesture = Gtk::GestureMultiPress;
286 constexpr auto SEQ_CLAIMED = Gtk::EVENT_SEQUENCE_CLAIMED;
287#endif
288
289 inline Glib::RefPtr<ClickGesture> add_click_gesture(Gtk::Widget& widget)
290 {
291#ifdef USE_GTK4
292 auto gesture = ClickGesture::create();
293 widget.add_controller(gesture);
294 return gesture;
295#else
296 return ClickGesture::create(widget);
297#endif
298 }
299
312
313 inline const char *CADABRA_CLIPBOARD_TARGET = "cadabra";
314
315 namespace clipboard_detail {
316
317 inline std::string& text_payload() { static std::string s; return s; }
318 inline std::string& cadabra_payload() { static std::string s; return s; }
319
320#ifndef USE_GTK4
321 inline void on_get(Gtk::SelectionData& selection_data, guint)
322 {
323 if(selection_data.get_target() == CADABRA_CLIPBOARD_TARGET)
324 selection_data.set(CADABRA_CLIPBOARD_TARGET, cadabra_payload());
325 else
326 selection_data.set_text(text_payload());
327 }
328
329 inline void on_clear() {}
330#endif
331 }
332
335
336 inline void clipboard_set(Gtk::Widget& widget, const std::string& text, const std::string& cadabra)
337 {
340
341#ifdef USE_GTK4
342 std::vector<Glib::RefPtr<Gdk::ContentProvider>> providers;
343 if(!cadabra.empty())
344 providers.push_back(
345 Gdk::ContentProvider::create(CADABRA_CLIPBOARD_TARGET,
346 Glib::Bytes::create(cadabra.data(), cadabra.size())));
347
348 Glib::Value<Glib::ustring> value;
349 value.init(Glib::Value<Glib::ustring>::value_type());
350 value.set(text);
351 providers.push_back(Gdk::ContentProvider::create(value));
352
353 widget.get_clipboard()->set_content(Gdk::ContentProvider::create(providers));
354#else
355 (void)widget;
356 std::vector<Gtk::TargetEntry> targets;
357 if(!cadabra.empty())
358 targets.push_back(Gtk::TargetEntry(CADABRA_CLIPBOARD_TARGET));
359 targets.push_back(Gtk::TargetEntry("UTF8_STRING"));
360 targets.push_back(Gtk::TargetEntry("TEXT"));
361
362 Gtk::Clipboard::get(GDK_SELECTION_CLIPBOARD)->set(
363 targets,
364 sigc::ptr_fun(&clipboard_detail::on_get),
365 sigc::ptr_fun(&clipboard_detail::on_clear));
366#endif
367 }
368
375
376 inline void clipboard_read(Gtk::Widget& widget, bool primary,
377 const std::function<void(const std::string&)>& on_received)
378 {
379#ifdef USE_GTK4
380 auto clipboard = primary ? widget.get_primary_clipboard() : widget.get_clipboard();
381
382 if(clipboard->get_formats()->contain_mime_type(CADABRA_CLIPBOARD_TARGET)) {
383 clipboard->read_async(
384 std::vector<Glib::ustring>{ CADABRA_CLIPBOARD_TARGET }, Glib::PRIORITY_DEFAULT,
385 [clipboard, on_received](const Glib::RefPtr<Gio::AsyncResult>& result) {
386 try {
387 Glib::ustring mime_type;
388 auto stream = clipboard->read_finish(result, mime_type);
389 if(!stream) return;
390
391 std::string content;
392 char buffer[4096];
393 gssize n;
394 while((n = stream->read(buffer, sizeof(buffer))) > 0)
395 content.append(buffer, n);
396 on_received(content);
397 }
398 catch(const Glib::Error&) { /* nothing on the clipboard for us */ }
399 });
400 return;
401 }
402
403 clipboard->read_text_async(
404 [clipboard, on_received](const Glib::RefPtr<Gio::AsyncResult>& result) {
405 try {
406 on_received(clipboard->read_text_finish(result));
407 }
408 catch(const Glib::Error&) { }
409 });
410#else
411 (void)widget;
412 auto clipboard = Gtk::Clipboard::get(primary ? GDK_SELECTION_PRIMARY : GDK_SELECTION_CLIPBOARD);
413 clipboard->request_targets(
414 [clipboard, on_received](const std::vector<Glib::ustring>& targets) {
415 std::string target;
416 for(const auto& t : targets) {
417 if(t == CADABRA_CLIPBOARD_TARGET) { target = t; break; }
418 if(target.empty() && (t == "UTF8_STRING" || t == "TEXT" || t == "STRING"))
419 target = t;
420 }
421 if(target.empty()) return;
422
423 clipboard->request_contents(target,
424 [on_received](const Gtk::SelectionData& data) {
425 on_received(data.get_data_as_string());
426 });
427 });
428#endif
429 }
430
438
440 public:
441 ContextMenu() = default;
442 ContextMenu(const ContextMenu&) = delete;
444
446 {
447#ifdef USE_GTK4
448 // A Gtk::Popover attached with set_parent() must be unparented
449 // before it goes away.
450 if(popover) {
451 popover->unparent();
452 delete popover;
453 }
454#endif
455 }
456
459 void attach_to(Gtk::Widget& widget, const Glib::ustring& action_prefix)
460 {
461#ifdef USE_GTK4
462 prefix = action_prefix;
463 model = Gio::Menu::create();
464 actions = Gio::SimpleActionGroup::create();
465 widget.insert_action_group(prefix, actions);
466
467 popover = new Gtk::PopoverMenu(model);
468 popover->set_parent(widget);
469 popover->set_has_arrow(false);
470#else
471 (void)widget;
472 (void)action_prefix;
473#endif
474 }
475
476 void add_item(const Glib::ustring& label, const sigc::slot<void()>& callback)
477 {
478#ifdef USE_GTK4
479 const Glib::ustring name = "item" + Glib::ustring::format(next_id++);
480 actions->add_action(name, callback);
481 model->append(label, prefix + "." + name);
482#else
483 auto* item = Gtk::make_managed<Gtk::MenuItem>(label);
484 item->signal_activate().connect(callback);
485 menu.append(*item);
486 item->show();
487#endif
488 }
489
491 void popup_at(double x, double y)
492 {
493#ifdef USE_GTK4
494 popover->set_pointing_to(Gdk::Rectangle(static_cast<int>(x), static_cast<int>(y), 1, 1));
495 popover->popup();
496#else
497 (void)x;
498 (void)y;
499 // nullptr means 'the event currently being processed'.
500 menu.popup_at_pointer(nullptr);
501#endif
502 }
503
504 private:
505#ifdef USE_GTK4
506 Glib::ustring prefix;
507 Glib::RefPtr<Gio::Menu> model;
508 Glib::RefPtr<Gio::SimpleActionGroup> actions;
509 Gtk::PopoverMenu *popover = nullptr;
510 int next_id = 0;
511#else
512 Gtk::Menu menu;
513#endif
514 };
515
518
519 template<typename T, typename U>
520 inline Glib::RefPtr<T> refptr_dynamic_cast(const Glib::RefPtr<U>& ptr)
521 {
522#ifdef USE_GTK4
523 return std::dynamic_pointer_cast<T>(ptr);
524#else
525 return Glib::RefPtr<T>::cast_dynamic(ptr);
526#endif
527 }
528
531
532 inline void window_centre_on_parent(Gtk::Window& window)
533 {
534#ifdef USE_GTK4
535 (void)window;
536#else
537 window.set_position(Gtk::WIN_POS_CENTER);
538#endif
539 }
540
542
543 inline void label_set_wrap(Gtk::Label& label, bool wrap = true)
544 {
545#ifdef USE_GTK4
546 label.set_wrap(wrap);
547#else
548 label.set_line_wrap(wrap);
549#endif
550 }
551
554
555 inline void set_margin_all(Gtk::Widget& widget, int margin)
556 {
557 widget.set_margin_start(margin);
558 widget.set_margin_end(margin);
559 widget.set_margin_top(margin);
560 widget.set_margin_bottom(margin);
561 }
562
564
565 inline void widget_size_allocate(Gtk::Widget& widget, const Gtk::Allocation& allocation)
566 {
567#ifdef USE_GTK4
568 widget.size_allocate(allocation, -1);
569#else
570 widget.size_allocate(allocation);
571#endif
572 }
573
586
587 inline bool translate_widget_coords(Gtk::Widget& src, Gtk::Widget& dest,
588 double src_x, double src_y,
589 double& dest_x, double& dest_y)
590 {
591#ifdef USE_GTK4
592 return src.translate_coordinates(dest, src_x, src_y, dest_x, dest_y);
593#else
594 int dx = 0, dy = 0;
595 if(!src.translate_coordinates(dest, static_cast<int>(src_x), static_cast<int>(src_y), dx, dy))
596 return false;
597 dest_x = dx;
598 dest_y = dy;
599 return true;
600#endif
601 }
602
616
617 inline int dialog_run(Gtk::Dialog& dialog); // defined further down
618
619 inline void about_dialog_run(Gtk::AboutDialog& about)
620 {
621#ifdef USE_GTK4
622 auto loop = Glib::MainLoop::create(false);
623
624 about.set_hide_on_close(true);
625 auto conn_hide = about.signal_hide().connect(
626 [loop]() {
627 if(loop->is_running())
628 loop->quit();
629 });
630
631 about.set_modal(true);
632 about.set_visible(true);
633 loop->run();
634
635 conn_hide.disconnect();
636 about.set_visible(false);
637#else
638 dialog_run(about);
639#endif
640 }
641
644
645 inline void about_dialog_set_logo(Gtk::AboutDialog& about, const Glib::RefPtr<Gdk::Pixbuf>& logo)
646 {
647#ifdef USE_GTK4
648 about.set_logo(Gdk::Texture::create_for_pixbuf(logo));
649#else
650 about.set_logo(logo);
651#endif
652 }
653
662
663 template<typename T>
664 inline Glib::RefPtr<T> refptr_for_instance(T* object)
665 {
666#ifdef USE_GTK4
667 return Glib::make_refptr_for_instance<T>(object);
668#else
669 return Glib::RefPtr<T>(object);
670#endif
671 }
672
675
676 inline bool css_load_from_data(const Glib::RefPtr<Gtk::CssProvider>& provider,
677 const std::string& data)
678 {
679#ifdef USE_GTK4
680 provider->load_from_data(data);
681 return true;
682#else
683 return provider->load_from_data(data);
684#endif
685 }
686
690
691 inline unsigned int box_child_count(Gtk::Box& box)
692 {
693#ifdef USE_GTK4
694 unsigned int n = 0;
695 for(auto *child = box.get_first_child(); child; child = child->get_next_sibling())
696 ++n;
697 return n;
698#else
699 return box.get_children().size();
700#endif
701 }
702
704
705 inline void box_reorder_child(Gtk::Box& box, Gtk::Widget& child, unsigned int index)
706 {
707#ifdef USE_GTK4
708 if(index==0) {
709 box.reorder_child_at_start(child);
710 return;
711 }
712 // Find the sibling that should end up just before `child`, skipping
713 // `child` itself so the index refers to the list without it.
714 Gtk::Widget *sibling = nullptr;
715 unsigned int seen = 0;
716 for(auto *c = box.get_first_child(); c; c = c->get_next_sibling()) {
717 if(c == &child) continue;
718 sibling = c;
719 if(++seen == index) break;
720 }
721 if(sibling)
722 box.reorder_child_after(child, *sibling);
723#else
724 box.reorder_child(child, index);
725#endif
726 }
727
730
731 inline void scrolled_window_unset_child(Gtk::ScrolledWindow& sw)
732 {
733#ifdef USE_GTK4
734 sw.unset_child();
735#else
736 sw.remove();
737#endif
738 }
739
740 inline void paned_unset_end(Gtk::Paned& paned)
741 {
742#ifdef USE_GTK4
743 paned.property_end_child() = nullptr;
744#else
745 if(auto *child = paned.get_child2())
746 paned.remove(*child);
747#endif
748 }
749
752
753 inline void show_now(Gtk::Widget& widget)
754 {
755#ifdef USE_GTK4
756 widget.set_visible(true);
757#else
758 widget.show_now();
759#endif
760 }
761
764
765 inline void file_chooser_confirm_overwrite(Gtk::FileChooser& chooser)
766 {
767#ifdef USE_GTK4
768 (void)chooser;
769#else
770 chooser.set_do_overwrite_confirmation(true);
771#endif
772 }
773
775
776 inline void add_css_provider_for_default_display(const Glib::RefPtr<Gtk::CssProvider>& provider,
777 guint priority)
778 {
779#ifdef USE_GTK4
780 Gtk::StyleContext::add_provider_for_display(Gdk::Display::get_default(), provider, priority);
781#else
782 Gtk::StyleContext::add_provider_for_screen(Gdk::Screen::get_default(), provider, priority);
783#endif
784 }
785
788
789 inline Gdk::Rectangle monitor_geometry(Gtk::Window& window)
790 {
791 auto display = Gdk::Display::get_default();
792 if(!display)
793 throw std::logic_error("cdb::monitor_geometry: cannot get default display.");
794
795#ifdef USE_GTK4
796 auto monitor = display->get_monitor_at_surface(window.get_surface());
797#else
798 auto monitor = display->get_monitor_at_window(window.get_window());
799#endif
800 if(!monitor)
801 throw std::logic_error("cdb::monitor_geometry: cannot get monitor for window.");
802
803 Gdk::Rectangle geometry;
804 monitor->get_geometry(geometry);
805 return geometry;
806 }
807
810
811 inline void set_dialog_type_hint(Gtk::Window& window)
812 {
813#ifdef USE_GTK4
814 (void)window;
815#else
816 window.set_type_hint(Gdk::WINDOW_TYPE_HINT_DIALOG);
817#endif
818 }
819
822
823 inline void paned_set_start(Gtk::Paned& paned, Gtk::Widget& child, bool resize, bool shrink)
824 {
825#ifdef USE_GTK4
826 paned.set_start_child(child);
827 paned.set_resize_start_child(resize);
828 paned.set_shrink_start_child(shrink);
829#else
830 paned.pack1(child, resize, shrink);
831#endif
832 }
833
834 inline void paned_set_end(Gtk::Paned& paned, Gtk::Widget& child, bool resize, bool shrink)
835 {
836#ifdef USE_GTK4
837 paned.set_end_child(child);
838 paned.set_resize_end_child(resize);
839 paned.set_shrink_end_child(shrink);
840#else
841 paned.pack2(child, resize, shrink);
842#endif
843 }
844
849
850 inline void show_all(Gtk::Widget& widget)
851 {
852#ifdef USE_GTK4
853 widget.set_visible(true);
854#else
855 widget.show_all();
856#endif
857 }
858
862
863 template<typename Container>
864 inline void show_all_children(Container& container)
865 {
866#ifdef USE_GTK4
867 (void)container;
868#else
869 container.show_all_children();
870#endif
871 }
872
877
878 template<typename Parent>
879 inline void add_child(Parent& parent, Gtk::Widget& child)
880 {
881#ifdef USE_GTK4
882 if constexpr (std::is_base_of_v<Gtk::Box, Parent>)
883 parent.append(child);
884 else
885 parent.set_child(child);
886#else
887 parent.add(child);
888 child.show();
889#endif
890 }
891
900
901 inline int dialog_run(Gtk::Dialog& dialog)
902 {
903#ifdef USE_GTK4
904 int response = static_cast<int>(Gtk::ResponseType::NONE);
905 auto loop = Glib::MainLoop::create(false);
906
907 auto conn_response = dialog.signal_response().connect(
908 [&response, loop](int id) {
909 if(loop->is_running()) {
910 response = id;
911 loop->quit();
912 }
913 });
914
915 // A dialog dismissed without emitting ::response (closed by the window
916 // manager, or hidden programmatically) would otherwise leave us stuck
917 // in the nested loop.
918 auto conn_hide = dialog.signal_hide().connect(
919 [loop]() {
920 if(loop->is_running())
921 loop->quit();
922 });
923
924 dialog.set_modal(true);
925 dialog.set_visible(true);
926 loop->run();
927
928 conn_response.disconnect();
929 conn_hide.disconnect();
930 dialog.set_visible(false);
931
932 return response;
933#else
934 return dialog.run();
935#endif
936 }
937
944
946
947 inline void set_accessible_role(Gtk::Widget& w, AccessibleRole role)
948 {
949#ifdef USE_GTK4
950 (void)w;
951 (void)role;
952 // FIXME: GTK4 needs Gtk::Accessible::Role passed at construction time.
953#else
954 auto obj = w.get_accessible();
955 if(!obj) return;
956 switch(role) {
957 case AccessibleRole::math: obj->set_role(Atk::ROLE_MATH); break;
958 case AccessibleRole::text: obj->set_role(Atk::ROLE_TEXT); break;
959 case AccessibleRole::push_button: obj->set_role(Atk::ROLE_PUSH_BUTTON); break;
960 }
961#endif
962 }
963
964 inline void set_accessible_name(Gtk::Widget& w, const Glib::ustring& name)
965 {
966#ifdef USE_GTK4
967 Glib::Value<Glib::ustring> value;
968 value.init(Glib::Value<Glib::ustring>::value_type());
969 value.set(name);
970 w.update_property(Gtk::Accessible::Property::LABEL, value);
971#else
972 auto obj = w.get_accessible();
973 if(obj)
974 obj->set_name(name);
975#endif
976 }
977
978 }
A right-click context menu.
Definition GtkCompat.hh:439
~ContextMenu()
Definition GtkCompat.hh:445
void add_item(const Glib::ustring &label, const sigc::slot< void()> &callback)
Definition GtkCompat.hh:476
Gtk::Menu menu
Definition GtkCompat.hh:512
ContextMenu(const ContextMenu &)=delete
void attach_to(Gtk::Widget &widget, const Glib::ustring &action_prefix)
Call once, before add_item().
Definition GtkCompat.hh:459
ContextMenu()=default
ContextMenu & operator=(const ContextMenu &)=delete
void popup_at(double x, double y)
x, y are relative to the widget passed to attach_to().
Definition GtkCompat.hh:491
Functions to handle the exchange properties of two or more symbols in a product.
Definition Adjform.cc:83
std::string & cadabra_payload()
Definition GtkCompat.hh:318
void on_get(Gtk::SelectionData &selection_data, guint)
Definition GtkCompat.hh:321
void on_clear()
Definition GtkCompat.hh:329
std::string & text_payload()
Definition GtkCompat.hh:317
Definition GtkCompat.hh:86
bool has_modifier(Gdk::ModifierType state, Gdk::ModifierType modifier)
Definition GtkCompat.hh:272
constexpr auto WRAP_NONE
Definition GtkCompat.hh:173
constexpr auto PANGO_TAB_LEFT
Definition GtkCompat.hh:197
constexpr auto SIZE_REQUEST_HEIGHT_FOR_WIDTH
Definition GtkCompat.hh:168
Gdk::Rectangle monitor_geometry(Gtk::Window &window)
The geometry of the monitor the window is on.
Definition GtkCompat.hh:789
void label_set_wrap(Gtk::Label &label, bool wrap=true)
Gtk::Label::set_line_wrap() is set_wrap() in GTK4.
Definition GtkCompat.hh:543
constexpr auto POLICY_ALWAYS
Definition GtkCompat.hh:164
bool css_load_from_data(const Glib::RefPtr< Gtk::CssProvider > &provider, const std::string &data)
Gtk::CssProvider::load_from_data() returns bool on gtkmm-3 but void on GTK4, where a parse failure is...
Definition GtkCompat.hh:676
void set_accessible_name(Gtk::Widget &w, const Glib::ustring &name)
Definition GtkCompat.hh:964
void show_all(Gtk::Widget &widget)
GTK4 removed show_all().
Definition GtkCompat.hh:850
constexpr auto APP_HANDLES_COMMAND_LINE
Definition GtkCompat.hh:102
void set_accessible_role(Gtk::Widget &w, AccessibleRole role)
Definition GtkCompat.hh:947
constexpr auto BUTTONS_YES_NO
Definition GtkCompat.hh:192
constexpr auto POLICY_NEVER
Definition GtkCompat.hh:166
AccessibleRole
Accessible roles we actually use.
Definition GtkCompat.hh:945
constexpr auto ORIENT_HORIZONTAL
Definition GtkCompat.hh:156
unsigned int box_child_count(Gtk::Box &box)
gtkmm-3's Gtk::Container API for inspecting and reordering children is gone in GTK4: there is no get_...
Definition GtkCompat.hh:691
void set_margin_all(Gtk::Widget &widget, int margin)
GTK4 has no single "margin" property, only the four sides.
Definition GtkCompat.hh:555
constexpr auto ALIGN_END
Definition GtkCompat.hh:161
void window_centre_on_parent(Gtk::Window &window)
Gtk::Window::set_position() is removed in GTK4, where placement is left to the compositor.
Definition GtkCompat.hh:532
constexpr auto COLORSPACE_RGB
Definition GtkCompat.hh:174
void paned_unset_end(Gtk::Paned &paned)
Definition GtkCompat.hh:740
void add_css_provider_for_default_display(const Glib::RefPtr< Gtk::CssProvider > &provider, guint priority)
Gdk::Screen is gone in GTK4; style providers attach to a Gdk::Display.
Definition GtkCompat.hh:776
void show_all_children(Container &container)
As above, but the gtkmm-3 form that shows the descendants without showing the widget itself.
Definition GtkCompat.hh:864
constexpr auto FILE_CHOOSER_ACTION_SAVE
Definition GtkCompat.hh:195
constexpr auto APP_HANDLES_OPEN
Unscoped gtkmm-3 enumerators became scoped enum classes in gtkmm-4, so the spellings differ even thou...
Definition GtkCompat.hh:101
void add_child(Parent &parent, Gtk::Widget &child)
Put child inside parent.
Definition GtkCompat.hh:879
constexpr auto MESSAGE_WARNING
Definition GtkCompat.hh:186
constexpr auto PANGO_UNDERLINE_ERROR
Definition GtkCompat.hh:198
Glib::RefPtr< ClickGesture > add_click_gesture(Gtk::Widget &widget)
Definition GtkCompat.hh:289
void paned_set_end(Gtk::Paned &paned, Gtk::Widget &child, bool resize, bool shrink)
Definition GtkCompat.hh:834
constexpr auto OPTION_TYPE_INT
Definition GtkCompat.hh:106
constexpr auto OPTION_TYPE_STRING
Definition GtkCompat.hh:107
constexpr auto TEXT_WINDOW_TEXT
Definition GtkCompat.hh:171
const char * CADABRA_CLIPBOARD_TARGET
Clipboard.
Definition GtkCompat.hh:313
void scrolled_window_unset_child(Gtk::ScrolledWindow &sw)
Detaching a child.
Definition GtkCompat.hh:731
constexpr auto RESPONSE_YES
Definition GtkCompat.hh:182
constexpr auto MESSAGE_QUESTION
Definition GtkCompat.hh:187
constexpr auto STATE_FLAG_SELECTED
Definition GtkCompat.hh:177
bool translate_widget_coords(Gtk::Widget &src, Gtk::Widget &dest, double src_x, double src_y, double &dest_x, double &dest_y)
Position of a point in src's coordinate space, expressed in dest's.
Definition GtkCompat.hh:587
constexpr auto RESPONSE_APPLY
Definition GtkCompat.hh:183
constexpr auto ALIGN_CENTER
Definition GtkCompat.hh:162
constexpr auto MODIFIER_SHIFT
Gdk::ModifierType became a scoped enum in GTK4, so both the enumerator spellings and the way a mask i...
Definition GtkCompat.hh:268
constexpr auto ALIGN_FILL
Definition GtkCompat.hh:159
void about_dialog_set_logo(Gtk::AboutDialog &about, const Glib::RefPtr< Gdk::Pixbuf > &logo)
GTK4's Gtk::AboutDialog::set_logo() takes a Gdk::Paintable rather than a Gdk::Pixbuf.
Definition GtkCompat.hh:645
Gtk::GestureMultiPress ClickGesture
Gtk::GestureMultiPress was renamed Gtk::GestureClick in GTK4, and the way a controller is attached to...
Definition GtkCompat.hh:285
void about_dialog_run(Gtk::AboutDialog &about)
Definition GtkCompat.hh:619
constexpr auto OPTION_TYPE_BOOL
Definition GtkCompat.hh:105
void set_dialog_type_hint(Gtk::Window &window)
Gtk::Window::set_type_hint() is removed in GTK4; the window manager is told what a dialog is via its ...
Definition GtkCompat.hh:811
Glib::RefPtr< T > refptr_for_instance(T *object)
Wrap a freshly-constructed Glib::Object subclass in a RefPtr.
Definition GtkCompat.hh:664
void widget_size_allocate(Gtk::Widget &widget, const Gtk::Allocation &allocation)
GTK4 added a baseline argument to Gtk::Widget::size_allocate().
Definition GtkCompat.hh:565
Glib::RefPtr< T > refptr_dynamic_cast(const Glib::RefPtr< U > &ptr)
glibmm-2.68 made Glib::RefPtr an alias for std::shared_ptr, which dropped the static RefPtr<T>::cast_...
Definition GtkCompat.hh:520
void box_append(Gtk::Box &box, Gtk::Widget &child, Expand expand=Expand::no, int padding=0)
Definition GtkCompat.hh:232
constexpr auto SIZE_REQUEST_WIDTH_FOR_HEIGHT
Definition GtkCompat.hh:169
int dialog_run(Gtk::Dialog &dialog)
Show the about box and block until the user dismisses it.
Definition GtkCompat.hh:901
constexpr auto RESPONSE_CLOSE
Definition GtkCompat.hh:181
void paned_set_start(Gtk::Paned &paned, Gtk::Widget &child, bool resize, bool shrink)
GTK4 replaced Gtk::Paned::pack1/pack2 with set_start_child/set_end_child plus separate resize/shrink ...
Definition GtkCompat.hh:823
constexpr auto POLICY_AUTOMATIC
Definition GtkCompat.hh:165
void show_now(Gtk::Widget &widget)
Gtk::Widget::show_now() is removed in GTK4; there is no way (nor need) to force a synchronous map.
Definition GtkCompat.hh:753
void file_chooser_confirm_overwrite(Gtk::FileChooser &chooser)
GTK4 removed Gtk::FileChooser::set_do_overwrite_confirmation(): a save dialog there always confirms a...
Definition GtkCompat.hh:765
constexpr auto SEQ_CLAIMED
Definition GtkCompat.hh:286
constexpr auto BUTTONS_OK
Definition GtkCompat.hh:191
constexpr auto ALIGN_START
Definition GtkCompat.hh:160
void clipboard_set(Gtk::Widget &widget, const std::string &text, const std::string &cadabra)
Put text on the clipboard, plus cadabra under the private target if it is non-empty.
Definition GtkCompat.hh:336
constexpr auto STATE_FLAG_NORMAL
Definition GtkCompat.hh:176
void clipboard_read(Gtk::Widget &widget, bool primary, const std::function< void(const std::string &)> &on_received)
Read the clipboard and hand the result to on_received.
Definition GtkCompat.hh:376
constexpr auto MESSAGE_ERROR
Definition GtkCompat.hh:188
constexpr auto MESSAGE_INFO
Definition GtkCompat.hh:185
constexpr auto RESPONSE_CANCEL
Definition GtkCompat.hh:180
constexpr auto WRAP_WORD_CHAR
Definition GtkCompat.hh:172
constexpr auto ORIENT_VERTICAL
Definition GtkCompat.hh:157
constexpr auto RESPONSE_OK
Definition GtkCompat.hh:179
constexpr auto BUTTONS_NONE
Definition GtkCompat.hh:190
Expand
GTK4 dissolved pack_start's three arguments into properties on the child: expand became set_h/vexpand...
Definition GtkCompat.hh:230
constexpr auto APP_NON_UNIQUE
Definition GtkCompat.hh:103
constexpr auto MODIFIER_CONTROL
Definition GtkCompat.hh:269
constexpr auto FILE_CHOOSER_ACTION_OPEN
Definition GtkCompat.hh:194
void box_reorder_child(Gtk::Box &box, Gtk::Widget &child, unsigned int index)
Move child to absolute position index among box's children.
Definition GtkCompat.hh:705
constexpr auto LICENSE_GPL_3_0
Definition GtkCompat.hh:196
Gtk::TextBuffer::iterator insert_image(const Glib::RefPtr< Gtk::TextBuffer > &buffer, const Gtk::TextBuffer::iterator &pos, const Glib::RefPtr< Gdk::Pixbuf > &pixbuf)
GTK4 replaced Gtk::TextBuffer::insert_pixbuf() with insert_paintable(); a Gdk::Pixbuf has to be wrapp...
Definition GtkCompat.hh:204