1 #include "MainWindow.h"
2 
3 #include <config-dev.h>
4 #include <config-features.h>
5 #include <config.h>
6 
7 #include "control/Control.h"
8 #include "control/layer/LayerController.h"
9 #include "gui/GladeSearchpath.h"
10 #include "gui/scroll/ScrollHandling.h"
11 #include "toolbarMenubar/ToolMenuHandler.h"
12 #include "toolbarMenubar/model/ToolbarData.h"
13 #include "toolbarMenubar/model/ToolbarModel.h"
14 #include "widgets/SpinPageAdapter.h"
15 #include "widgets/XournalWidget.h"
16 
17 #include "Layout.h"
18 #include "MainWindowToolbarMenu.h"
19 #include "ToolbarDefinitions.h"
20 #include "ToolitemDragDrop.h"
21 #include "XojMsgBox.h"
22 #include "XournalView.h"
23 #include "i18n.h"
24 
25 #include <utility>
26 
27 #include <gdk/gdk.h>
28 
29 #include "gui/inputdevices/InputEvents.h"
30 #include "util/DeviceListHelper.h"
31 
MainWindow(GladeSearchpath * gladeSearchPath,Control * control)32 MainWindow::MainWindow(GladeSearchpath* gladeSearchPath, Control* control):
33         GladeGui(gladeSearchPath, "main.glade", "mainWindow") {
34     this->control = control;
35     this->toolbarWidgets = new GtkWidget*[TOOLBAR_DEFINITIONS_LEN];
36     this->toolbarSelectMenu = new MainWindowToolbarMenu(this);
37 
38     panedContainerWidget = GTK_WIDGET(get("panelMainContents"));
39     boxContainerWidget = GTK_WIDGET(get("mainContentContainer"));
40     mainContentWidget = GTK_WIDGET(get("boxContents"));
41     sidebarWidget = GTK_WIDGET(get("sidebar"));
42     g_object_ref(panedContainerWidget);
43     g_object_ref(boxContainerWidget);
44     g_object_ref(mainContentWidget);
45     g_object_ref(sidebarWidget);
46 
47     loadMainCSS(gladeSearchPath, "xournalpp.css");
48 
49     GtkOverlay* overlay = GTK_OVERLAY(get("mainOverlay"));
50     this->floatingToolbox = new FloatingToolbox(this, overlay);
51 
52 
53     for (int i = 0; i < TOOLBAR_DEFINITIONS_LEN; i++) {
54         GtkWidget* w = get(TOOLBAR_DEFINITIONS[i].guiName);
55         g_object_ref(w);
56         this->toolbarWidgets[i] = w;
57     }
58 
59     initXournalWidget();
60 
61     setSidebarVisible(control->getSettings()->isSidebarVisible());
62 
63     // Window handler
64     g_signal_connect(this->window, "delete-event", G_CALLBACK(deleteEventCallback), this->control);
65     g_signal_connect(this->window, "window_state_event", G_CALLBACK(windowStateEventCallback), this);
66 
67     g_signal_connect(get("buttonCloseSidebar"), "clicked", G_CALLBACK(buttonCloseSidebarClicked), this);
68 
69 
70     // "watch over" all events
71     g_signal_connect(this->window, "key-press-event", G_CALLBACK(onKeyPressCallback), this);
72 
73     this->toolbar = new ToolMenuHandler(this->control, this, GTK_WINDOW(getWindow()));
74 
75     auto file = gladeSearchPath->findFile("", "toolbar.ini");
76 
77     ToolbarModel* tbModel = this->toolbar->getModel();
78 
79     if (!tbModel->parse(file, true)) {
80 
81         string msg = FS(_F("Could not parse general toolbar.ini file: {1}\n"
82                            "No Toolbars will be available") %
83                         file.u8string());
84         XojMsgBox::showErrorToUser(control->getGtkWindow(), msg);
85     }
86 
87     file = Util::getConfigFile(TOOLBAR_CONFIG);
88     if (fs::exists(file)) {
89         if (!tbModel->parse(file, false)) {
90             string msg = FS(_F("Could not parse custom toolbar.ini file: {1}\n"
91                                "Toolbars will not be available") %
92                             file.u8string());
93             XojMsgBox::showErrorToUser(control->getGtkWindow(), msg);
94         }
95     }
96 
97     createToolbarAndMenu();
98 
99     setToolbarVisible(control->getSettings()->isToolbarVisible());
100 
101     GtkWidget* menuViewSidebarVisible = get("menuViewSidebarVisible");
102     g_signal_connect(menuViewSidebarVisible, "toggled", G_CALLBACK(viewShowSidebar), this);
103 
104     GtkWidget* menuViewToolbarsVisible = get("menuViewToolbarsVisible");
105     g_signal_connect(menuViewToolbarsVisible, "toggled", G_CALLBACK(viewShowToolbar), this);
106 
107     updateScrollbarSidebarPosition();
108 
109     gtk_window_set_default_size(GTK_WINDOW(this->window), control->getSettings()->getMainWndWidth(),
110                                 control->getSettings()->getMainWndHeight());
111 
112     if (control->getSettings()->isMainWndMaximized()) {
113         gtk_window_maximize(GTK_WINDOW(this->window));
114     } else {
115         gtk_window_unmaximize(GTK_WINDOW(this->window));
116     }
117 
118     getSpinPageNo()->addListener(this->control->getScrollHandler());
119 
120 
121     Util::execInUiThread([=]() {
122         // Execute after the window is visible, else the check won't work
123         initHideMenu();
124     });
125 
126     // Drag and Drop
127     g_signal_connect(this->window, "drag-data-received", G_CALLBACK(dragDataRecived), this);
128 
129     gtk_drag_dest_set(this->window, GTK_DEST_DEFAULT_ALL, nullptr, 0, GDK_ACTION_COPY);
130     gtk_drag_dest_add_uri_targets(this->window);
131     gtk_drag_dest_add_image_targets(this->window);
132     gtk_drag_dest_add_text_targets(this->window);
133 
134     LayerCtrlListener::registerListener(control->getLayerController());
135 }
136 
isKeyForClosure(GtkAccelKey * key,GClosure * closure,gpointer data)137 gboolean MainWindow::isKeyForClosure(GtkAccelKey* key, GClosure* closure, gpointer data) { return closure == data; }
138 
invokeMenu(GtkWidget * widget)139 gboolean MainWindow::invokeMenu(GtkWidget* widget) {
140     // g_warning("invoke_menu %s", gtk_widget_get_name(widget));
141     gtk_widget_activate(widget);
142     return TRUE;
143 }
144 
rebindAcceleratorsMenuItem(GtkWidget * widget,gpointer user_data)145 void MainWindow::rebindAcceleratorsMenuItem(GtkWidget* widget, gpointer user_data) {
146     if (GTK_IS_MENU_ITEM(widget)) {
147         GtkAccelGroup* newAccelGroup = reinterpret_cast<GtkAccelGroup*>(user_data);
148         GList* menuAccelClosures = gtk_widget_list_accel_closures(widget);
149         for (GList* l = menuAccelClosures; l != nullptr; l = l->next) {
150             GClosure* closure = reinterpret_cast<GClosure*>(l->data);
151             GtkAccelGroup* accelGroup = gtk_accel_group_from_accel_closure(closure);
152             GtkAccelKey* key = gtk_accel_group_find(accelGroup, isKeyForClosure, closure);
153 
154             // g_warning("Rebind %s : %s", gtk_accelerator_get_label(key->accel_key, key->accel_mods),
155             // gtk_widget_get_name(widget));
156 
157             gtk_accel_group_connect(newAccelGroup, key->accel_key, key->accel_mods, GtkAccelFlags(0),
158                                     g_cclosure_new_swap(G_CALLBACK(MainWindow::invokeMenu), widget, nullptr));
159         }
160 
161         MainWindow::rebindAcceleratorsSubMenu(widget, newAccelGroup);
162     }
163 }
164 
rebindAcceleratorsSubMenu(GtkWidget * widget,gpointer user_data)165 void MainWindow::rebindAcceleratorsSubMenu(GtkWidget* widget, gpointer user_data) {
166     if (GTK_IS_MENU_ITEM(widget)) {
167         GtkMenuItem* menuItem = reinterpret_cast<GtkMenuItem*>(widget);
168         GtkWidget* subMenu = gtk_menu_item_get_submenu(menuItem);
169         if (GTK_IS_CONTAINER(subMenu)) {
170             gtk_container_foreach(reinterpret_cast<GtkContainer*>(subMenu), rebindAcceleratorsMenuItem, user_data);
171         }
172     }
173 }
174 
175 // When the Menubar is hidden, accelerators no longer work so rebind them to the MainWindow
176 // It should be called after all plugins have been initialised so that their injected menu items are captured
rebindMenubarAccelerators()177 void MainWindow::rebindMenubarAccelerators() {
178     this->globalAccelGroup = gtk_accel_group_new();
179     gtk_window_add_accel_group(GTK_WINDOW(this->getWindow()), this->globalAccelGroup);
180 
181     GtkMenuBar* menuBar = (GtkMenuBar*)this->get("mainMenubar");
182     gtk_container_foreach(reinterpret_cast<GtkContainer*>(menuBar), rebindAcceleratorsSubMenu, this->globalAccelGroup);
183 }
184 
~MainWindow()185 MainWindow::~MainWindow() {
186     for (int i = 0; i < TOOLBAR_DEFINITIONS_LEN; i++) {
187         g_object_unref(this->toolbarWidgets[i]);
188     }
189 
190     delete[] this->toolbarWidgets;
191     this->toolbarWidgets = nullptr;
192 
193     delete this->toolbarSelectMenu;
194     this->toolbarSelectMenu = nullptr;
195 
196     delete this->floatingToolbox;
197     this->floatingToolbox = nullptr;
198 
199     delete this->xournal;
200     this->xournal = nullptr;
201 
202     delete this->toolbar;
203     this->toolbar = nullptr;
204 
205     delete scrollHandling;
206     scrollHandling = nullptr;
207 
208     g_object_unref(panedContainerWidget);
209     g_object_unref(boxContainerWidget);
210     g_object_unref(mainContentWidget);
211     g_object_unref(sidebarWidget);
212 }
213 
214 /**
215  * Topmost widgets, to check if there is a menu above
216  */
217 const char* TOP_WIDGETS[] = {"tbTop1", "tbTop2", "mainContainerBox", nullptr};
218 
219 
toggleMenuBar(MainWindow * win)220 void MainWindow::toggleMenuBar(MainWindow* win) {
221     GtkWidget* menu = win->get("mainMenubar");
222     if (gtk_widget_is_visible(menu)) {
223         gtk_widget_hide(menu);
224     } else {
225         gtk_widget_show(menu);
226     }
227 }
228 
updateColorscheme()229 void MainWindow::updateColorscheme() {
230     bool darkMode = control->getSettings()->isDarkTheme();
231     GtkStyleContext* context = gtk_widget_get_style_context(GTK_WIDGET(this->window));
232 
233     if (darkMode) {
234         gtk_style_context_add_class(context, "darkMode");
235     } else {
236         gtk_style_context_remove_class(context, "darkMode");
237     }
238 }
239 
initXournalWidget()240 void MainWindow::initXournalWidget() {
241     GtkWidget* boxContents = get("boxContents");
242 
243 
244     winXournal = gtk_scrolled_window_new(nullptr, nullptr);
245 
246     setGtkTouchscreenScrollingForDeviceMapping();
247 
248     gtk_container_add(GTK_CONTAINER(boxContents), winXournal);
249 
250     GtkWidget* vpXournal = gtk_viewport_new(nullptr, nullptr);
251 
252     gtk_container_add(GTK_CONTAINER(winXournal), vpXournal);
253 
254     scrollHandling = new ScrollHandling(GTK_SCROLLABLE(vpXournal));
255 
256     this->xournal = new XournalView(vpXournal, control, scrollHandling);
257 
258     control->getZoomControl()->initZoomHandler(this->window, winXournal, xournal, control);
259     gtk_widget_show_all(winXournal);
260 
261     Layout* layout = gtk_xournal_get_layout(this->xournal->getWidget());
262     scrollHandling->init(this->xournal->getWidget(), layout);
263 
264     updateColorscheme();
265 }
266 
setGtkTouchscreenScrollingForDeviceMapping()267 void MainWindow::setGtkTouchscreenScrollingForDeviceMapping() {
268     InputDeviceClass touchscreenClass =
269             DeviceListHelper::getSourceMapping(GDK_SOURCE_TOUCHSCREEN, this->getControl()->getSettings());
270 
271     setGtkTouchscreenScrollingEnabled(touchscreenClass == INPUT_DEVICE_TOUCHSCREEN &&
272                                       !control->getSettings()->getTouchDrawingEnabled());
273 }
274 
setGtkTouchscreenScrollingEnabled(bool enabled)275 void MainWindow::setGtkTouchscreenScrollingEnabled(bool enabled) {
276     if (enabled == gtkTouchscreenScrollingEnabled.load() || winXournal == nullptr) {
277         return;
278     }
279 
280     gtkTouchscreenScrollingEnabled.store(enabled);
281 
282     Util::execInUiThread(
283             [=]() {
284                 gtk_scrolled_window_set_kinetic_scrolling(GTK_SCROLLED_WINDOW(winXournal),
285                                                           gtkTouchscreenScrollingEnabled.load());
286             },
287             G_PRIORITY_HIGH);
288 }
289 
getGtkTouchscreenScrollingEnabled() const290 bool MainWindow::getGtkTouchscreenScrollingEnabled() const { return gtkTouchscreenScrollingEnabled.load(); }
291 
292 /**
293  * Allow to hide menubar, but only if global menu is not enabled
294  */
initHideMenu()295 void MainWindow::initHideMenu() {
296     int top = -1;
297     for (int i = 0; TOP_WIDGETS[i]; i++) {
298         GtkWidget* w = get(TOP_WIDGETS[i]);
299         GtkAllocation allocation;
300         gtk_widget_get_allocation(w, &allocation);
301         if (allocation.y != -1) {
302             top = allocation.y;
303             break;
304         }
305     }
306 
307     GtkWidget* menuItem = get("menuHideMenu");
308     if (top < 5) {
309         // There is no menu to hide, the menu is in the globalmenu!
310         gtk_widget_hide(menuItem);
311     } else {
312         // Menu found, allow to hide it
313         g_signal_connect(menuItem, "activate",
314                          G_CALLBACK(+[](GtkMenuItem* menuitem, MainWindow* self) { toggleMenuBar(self); }), this);
315     }
316 
317     // Hide menubar at startup if specified in settings
318     Settings* settings = control->getSettings();
319     if (settings && !settings->isMenubarVisible()) {
320         toggleMenuBar(this);
321     }
322 }
323 
getLayout()324 auto MainWindow::getLayout() -> Layout* { return gtk_xournal_get_layout(GTK_WIDGET(this->xournal->getWidget())); }
325 
cancellable_cancel(GCancellable * cancel)326 auto cancellable_cancel(GCancellable* cancel) -> bool {
327     g_cancellable_cancel(cancel);
328 
329     g_warning("Timeout... Cancel loading URL");
330 
331     return false;
332 }
333 
dragDataRecived(GtkWidget * widget,GdkDragContext * dragContext,gint x,gint y,GtkSelectionData * data,guint info,guint time,MainWindow * win)334 void MainWindow::dragDataRecived(GtkWidget* widget, GdkDragContext* dragContext, gint x, gint y, GtkSelectionData* data,
335                                  guint info, guint time, MainWindow* win) {
336     GtkWidget* source = gtk_drag_get_source_widget(dragContext);
337     if (source && widget == gtk_widget_get_toplevel(source)) {
338         gtk_drag_finish(dragContext, false, false, time);
339         return;
340     }
341 
342     guchar* text = gtk_selection_data_get_text(data);
343     if (text) {
344         win->control->clipboardPasteText(reinterpret_cast<const char*>(text));
345 
346         g_free(text);
347         gtk_drag_finish(dragContext, true, false, time);
348         return;
349     }
350 
351     GdkPixbuf* image = gtk_selection_data_get_pixbuf(data);
352     if (image) {
353         win->control->clipboardPasteImage(image);
354 
355         g_object_unref(image);
356         gtk_drag_finish(dragContext, true, false, time);
357         return;
358     }
359 
360     gchar** uris = gtk_selection_data_get_uris(data);
361     if (uris) {
362         for (int i = 0; uris[i] != nullptr && i < 3; i++) {
363             const char* uri = uris[i];
364 
365             GCancellable* cancel = g_cancellable_new();
366             int cancelTimeout = g_timeout_add(3000, reinterpret_cast<GSourceFunc>(cancellable_cancel), cancel);
367 
368             GFile* file = g_file_new_for_uri(uri);
369             GError* err = nullptr;
370             GFileInputStream* in = g_file_read(file, cancel, &err);
371             if (g_cancellable_is_cancelled(cancel)) {
372                 continue;
373             }
374 
375             g_object_unref(file);
376             if (err == nullptr) {
377                 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_stream(G_INPUT_STREAM(in), cancel, nullptr);
378                 if (g_cancellable_is_cancelled(cancel)) {
379                     continue;
380                 }
381                 g_input_stream_close(G_INPUT_STREAM(in), cancel, nullptr);
382                 if (g_cancellable_is_cancelled(cancel)) {
383                     continue;
384                 }
385 
386                 if (pixbuf) {
387                     win->control->clipboardPasteImage(pixbuf);
388 
389                     g_object_unref(pixbuf);
390                 }
391             } else {
392                 g_error_free(err);
393             }
394 
395             if (!g_cancellable_is_cancelled(cancel)) {
396                 g_source_remove(cancelTimeout);
397             }
398             g_object_unref(cancel);
399         }
400 
401         gtk_drag_finish(dragContext, true, false, time);
402 
403         g_strfreev(uris);
404     }
405 
406     gtk_drag_finish(dragContext, false, false, time);
407 }
408 
viewShowSidebar(GtkCheckMenuItem * checkmenuitem,MainWindow * win)409 void MainWindow::viewShowSidebar(GtkCheckMenuItem* checkmenuitem, MainWindow* win) {
410     bool a = gtk_check_menu_item_get_active(checkmenuitem);
411     if (win->control->getSettings()->isSidebarVisible() == a) {
412         return;
413     }
414     win->setSidebarVisible(a);
415 }
416 
viewShowToolbar(GtkCheckMenuItem * checkmenuitem,MainWindow * win)417 void MainWindow::viewShowToolbar(GtkCheckMenuItem* checkmenuitem, MainWindow* win) {
418     bool showToolbar = gtk_check_menu_item_get_active(checkmenuitem);
419     if (win->control->getSettings()->isToolbarVisible() == showToolbar) {
420         return;
421     }
422     win->setToolbarVisible(showToolbar);
423 }
424 
getControl()425 auto MainWindow::getControl() -> Control* { return control; }
426 
updateScrollbarSidebarPosition()427 void MainWindow::updateScrollbarSidebarPosition() {
428     GtkWidget* panelMainContents = get("panelMainContents");
429 
430     if (winXournal != nullptr) {
431         GtkScrolledWindow* scrolledWindow = GTK_SCROLLED_WINDOW(winXournal);
432 
433         ScrollbarHideType type = this->getControl()->getSettings()->getScrollbarHideType();
434 
435         bool scrollbarOnLeft = control->getSettings()->isScrollbarOnLeft();
436         if (scrollbarOnLeft) {
437             gtk_scrolled_window_set_placement(scrolledWindow, GTK_CORNER_TOP_RIGHT);
438         } else {
439             gtk_scrolled_window_set_placement(scrolledWindow, GTK_CORNER_TOP_LEFT);
440         }
441 
442         gtk_widget_set_visible(gtk_scrolled_window_get_hscrollbar(scrolledWindow), !(type & SCROLLBAR_HIDE_HORIZONTAL));
443         gtk_widget_set_visible(gtk_scrolled_window_get_vscrollbar(scrolledWindow), !(type & SCROLLBAR_HIDE_VERTICAL));
444 
445         gtk_scrolled_window_set_overlay_scrolling(scrolledWindow,
446                                                   !control->getSettings()->isScrollbarFadeoutDisabled());
447     }
448 
449     // If the sidebar isn't visible, we can't change its position!
450     if (!this->sidebarVisible) {
451         return;
452     }
453 
454     GtkWidget* sidebar = get("sidebar");
455     GtkWidget* boxContents = get("boxContents");
456 
457     int divider = gtk_paned_get_position(GTK_PANED(panelMainContents));
458     bool sidebarRight = control->getSettings()->isSidebarOnRight();
459     if (sidebarRight == (gtk_paned_get_child2(GTK_PANED(panelMainContents)) == sidebar)) {
460         // Already correct
461         return;
462     }
463 
464 
465     GtkAllocation allocation;
466     gtk_widget_get_allocation(panelMainContents, &allocation);
467     divider = allocation.width - divider;
468 
469 
470     g_object_ref(sidebar);
471     g_object_ref(boxContents);
472 
473     gtk_container_remove(GTK_CONTAINER(panelMainContents), sidebar);
474     gtk_container_remove(GTK_CONTAINER(panelMainContents), boxContents);
475 
476     if (sidebarRight) {
477         gtk_paned_pack1(GTK_PANED(panelMainContents), boxContents, true, false);
478         gtk_paned_pack2(GTK_PANED(panelMainContents), sidebar, false, false);
479     } else {
480         gtk_paned_pack1(GTK_PANED(panelMainContents), sidebar, false, false);
481         gtk_paned_pack2(GTK_PANED(panelMainContents), boxContents, true, false);
482     }
483 
484     gtk_paned_set_position(GTK_PANED(panelMainContents), divider);
485     g_object_unref(sidebar);
486     g_object_unref(boxContents);
487 }
488 
buttonCloseSidebarClicked(GtkButton * button,MainWindow * win)489 void MainWindow::buttonCloseSidebarClicked(GtkButton* button, MainWindow* win) { win->setSidebarVisible(false); }
490 
onKeyPressCallback(GtkWidget * widget,GdkEventKey * event,MainWindow * win)491 auto MainWindow::onKeyPressCallback(GtkWidget* widget, GdkEventKey* event, MainWindow* win) -> bool {
492 
493     if (win->getXournal()->getSelection()) {
494         // something is selected - give that control
495         return false;
496     }
497     if (win->getXournal()->getTextEditor()) {
498         // editing text - give that control
499         return false;
500     }
501     if (event->keyval == GDK_KEY_Escape) {
502         win->getControl()->getSearchBar()->showSearchBar(false);
503         return true;
504     }
505 
506 
507     return false;
508 }
509 
deleteEventCallback(GtkWidget * widget,GdkEvent * event,Control * control)510 auto MainWindow::deleteEventCallback(GtkWidget* widget, GdkEvent* event, Control* control) -> bool {
511     control->quit();
512 
513     return true;
514 }
515 
setSidebarVisible(bool visible)516 void MainWindow::setSidebarVisible(bool visible) {
517     Settings* settings = control->getSettings();
518 
519     settings->setSidebarVisible(visible);
520     if (!visible && (control->getSidebar() != nullptr)) {
521         saveSidebarSize();
522     }
523 
524     if (visible != this->sidebarVisible) {
525         // Due to a GTK bug, we can't just hide the sidebar widget in the GtkPaned.
526         // If we do this, we create a dead region where the pane separator was previously.
527         // In this region, we can't use the touchscreen to start horizontal strokes.
528         // As such:
529         if (!visible) {
530             gtk_container_remove(GTK_CONTAINER(panedContainerWidget), mainContentWidget);
531             gtk_container_remove(GTK_CONTAINER(boxContainerWidget), GTK_WIDGET(panedContainerWidget));
532             gtk_container_add(GTK_CONTAINER(boxContainerWidget), mainContentWidget);
533             this->sidebarVisible = false;
534         } else {
535             gtk_container_remove(GTK_CONTAINER(boxContainerWidget), mainContentWidget);
536             gtk_container_add(GTK_CONTAINER(panedContainerWidget), mainContentWidget);
537             gtk_container_add(GTK_CONTAINER(boxContainerWidget), GTK_WIDGET(panedContainerWidget));
538             this->sidebarVisible = true;
539 
540             updateScrollbarSidebarPosition();
541         }
542     }
543 
544     gtk_widget_set_visible(sidebarWidget, visible);
545 
546     if (visible) {
547         gtk_paned_set_position(GTK_PANED(panedContainerWidget), settings->getSidebarWidth());
548     }
549 
550     GtkWidget* w = get("menuViewSidebarVisible");
551     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w), visible);
552 }
553 
setToolbarVisible(bool visible)554 void MainWindow::setToolbarVisible(bool visible) {
555     Settings* settings = control->getSettings();
556 
557     settings->setToolbarVisible(visible);
558     for (int i = 0; i < TOOLBAR_DEFINITIONS_LEN; i++) {
559         auto widget = this->toolbarWidgets[i];
560         if (!visible || GTK_IS_CONTAINER(widget) && gtk_container_get_children(GTK_CONTAINER(widget))) {
561             gtk_widget_set_visible(widget, visible);
562         }
563     }
564 
565     GtkWidget* w = get("menuViewToolbarsVisible");
566     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w), visible);
567 }
568 
saveSidebarSize()569 void MainWindow::saveSidebarSize() {
570     this->control->getSettings()->setSidebarWidth(gtk_paned_get_position(GTK_PANED(panedContainerWidget)));
571 }
572 
setMaximized(bool maximized)573 void MainWindow::setMaximized(bool maximized) { this->maximized = maximized; }
574 
isMaximized() const575 auto MainWindow::isMaximized() const -> bool { return this->maximized; }
576 
getXournal()577 auto MainWindow::getXournal() -> XournalView* { return xournal; }
578 
windowStateEventCallback(GtkWidget * window,GdkEventWindowState * event,MainWindow * win)579 auto MainWindow::windowStateEventCallback(GtkWidget* window, GdkEventWindowState* event, MainWindow* win) -> bool {
580     win->setMaximized(gtk_window_is_maximized(GTK_WINDOW(window)));
581 
582     return false;
583 }
584 
reloadToolbars()585 void MainWindow::reloadToolbars() {
586     bool inDragAndDrop = this->control->isInDragAndDropToolbar();
587 
588     ToolbarData* d = getSelectedToolbar();
589 
590     if (inDragAndDrop) {
591         this->control->endDragDropToolbar();
592     }
593 
594     this->clearToolbar();
595     this->toolbarSelected(d);
596 
597     if (inDragAndDrop) {
598         this->control->startDragDropToolbar();
599     }
600 }
601 
toolbarSelected(ToolbarData * d)602 void MainWindow::toolbarSelected(ToolbarData* d) {
603     if (!this->toolbarIntialized || this->selectedToolbar == d) {
604         return;
605     }
606 
607     Settings* settings = control->getSettings();
608     settings->setSelectedToolbar(d->getId());
609 
610     this->clearToolbar();
611     this->loadToolbar(d);
612 }
613 
clearToolbar()614 auto MainWindow::clearToolbar() -> ToolbarData* {
615     if (this->selectedToolbar != nullptr) {
616         for (int i = 0; i < TOOLBAR_DEFINITIONS_LEN; i++) {
617             ToolMenuHandler::unloadToolbar(this->toolbarWidgets[i]);
618         }
619 
620         this->toolbar->freeDynamicToolbarItems();
621     }
622 
623     ToolbarData* oldData = this->selectedToolbar;
624 
625     this->selectedToolbar = nullptr;
626 
627     return oldData;
628 }
629 
loadToolbar(ToolbarData * d)630 void MainWindow::loadToolbar(ToolbarData* d) {
631     this->selectedToolbar = d;
632 
633     for (int i = 0; i < TOOLBAR_DEFINITIONS_LEN; i++) {
634         this->toolbar->load(d, this->toolbarWidgets[i], TOOLBAR_DEFINITIONS[i].propName,
635                             TOOLBAR_DEFINITIONS[i].horizontal);
636     }
637 
638     this->floatingToolbox->flagRecalculateSizeRequired();
639 }
640 
getSelectedToolbar()641 auto MainWindow::getSelectedToolbar() -> ToolbarData* { return this->selectedToolbar; }
642 
getToolbarWidgets(int & length)643 auto MainWindow::getToolbarWidgets(int& length) -> GtkWidget** {
644     length = TOOLBAR_DEFINITIONS_LEN;
645     return this->toolbarWidgets;
646 }
647 
getToolbarName(GtkToolbar * toolbar)648 auto MainWindow::getToolbarName(GtkToolbar* toolbar) -> const char* {
649     for (int i = 0; i < TOOLBAR_DEFINITIONS_LEN; i++) {
650         if (static_cast<void*>(this->toolbarWidgets[i]) == static_cast<void*>(toolbar)) {
651             return TOOLBAR_DEFINITIONS[i].propName;
652         }
653     }
654 
655     return "";
656 }
657 
setControlTmpDisabled(bool disabled)658 void MainWindow::setControlTmpDisabled(bool disabled) {
659     toolbar->setTmpDisabled(disabled);
660     toolbarSelectMenu->setTmpDisabled(disabled);
661 
662     GtkWidget* menuFileRecent = get("menuFileRecent");
663     gtk_widget_set_sensitive(menuFileRecent, !disabled);
664 }
665 
updateToolbarMenu()666 void MainWindow::updateToolbarMenu() { createToolbarAndMenu(); }
667 
createToolbarAndMenu()668 void MainWindow::createToolbarAndMenu() {
669     GtkMenuShell* menubar = GTK_MENU_SHELL(get("menuViewToolbar"));
670     g_return_if_fail(menubar != nullptr);
671 
672     toolbarSelectMenu->updateToolbarMenu(menubar, control->getSettings(), toolbar);
673 
674     ToolbarData* td = toolbarSelectMenu->getSelectedToolbar();
675     if (td) {
676         this->toolbarIntialized = true;
677         toolbarSelected(td);
678     }
679 
680     if (!this->control->getAudioController()->isPlaying()) {
681         this->getToolMenuHandler()->disableAudioPlaybackButtons();
682     }
683 
684     this->control->getScheduler()->unblockRerenderZoom();
685 }
686 
setFontButtonFont(XojFont & font)687 void MainWindow::setFontButtonFont(XojFont& font) { toolbar->setFontButtonFont(font); }
688 
getFontButtonFont()689 auto MainWindow::getFontButtonFont() -> XojFont { return toolbar->getFontButtonFont(); }
690 
updatePageNumbers(size_t page,size_t pagecount,size_t pdfpage)691 void MainWindow::updatePageNumbers(size_t page, size_t pagecount, size_t pdfpage) {
692     SpinPageAdapter* spinPageNo = getSpinPageNo();
693 
694     size_t min = 0;
695     size_t max = pagecount;
696 
697     if (pagecount == 0) {
698         min = 0;
699         page = 0;
700     } else {
701         min = 1;
702         page++;
703     }
704 
705     spinPageNo->setMinMaxPage(min, max);
706     spinPageNo->setPage(page);
707 
708     if (pdfpage != npos) {
709         toolbar->setPageInfo(pagecount, pdfpage + 1);
710     } else {
711         toolbar->setPageInfo(pagecount);
712     }
713 }
714 
rebuildLayerMenu()715 void MainWindow::rebuildLayerMenu() { layerVisibilityChanged(); }
716 
layerVisibilityChanged()717 void MainWindow::layerVisibilityChanged() {
718     LayerController* lc = control->getLayerController();
719 
720     int layer = lc->getCurrentLayerId();
721     int maxLayer = lc->getLayerCount();
722 
723     control->fireEnableAction(ACTION_DELETE_LAYER, layer > 0);
724     control->fireEnableAction(ACTION_GOTO_NEXT_LAYER, layer < maxLayer);
725     control->fireEnableAction(ACTION_GOTO_PREVIOUS_LAYER, layer > 0);
726     control->fireEnableAction(ACTION_GOTO_TOP_LAYER, layer < maxLayer);
727 }
728 
setRecentMenu(GtkWidget * submenu)729 void MainWindow::setRecentMenu(GtkWidget* submenu) {
730     GtkWidget* menuitem = get("menuFileRecent");
731     g_return_if_fail(menuitem != nullptr);
732     gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
733 }
734 
show(GtkWindow * parent)735 void MainWindow::show(GtkWindow* parent) { gtk_widget_show(this->window); }
736 
setUndoDescription(const string & description)737 void MainWindow::setUndoDescription(const string& description) { toolbar->setUndoDescription(description); }
738 
setRedoDescription(const string & description)739 void MainWindow::setRedoDescription(const string& description) { toolbar->setRedoDescription(description); }
740 
getSpinPageNo()741 auto MainWindow::getSpinPageNo() -> SpinPageAdapter* { return toolbar->getPageSpinner(); }
742 
getToolbarModel()743 auto MainWindow::getToolbarModel() -> ToolbarModel* { return this->toolbar->getModel(); }
744 
getToolMenuHandler()745 auto MainWindow::getToolMenuHandler() -> ToolMenuHandler* { return this->toolbar; }
746 
disableAudioPlaybackButtons()747 void MainWindow::disableAudioPlaybackButtons() {
748     setAudioPlaybackPaused(false);
749 
750     this->getToolMenuHandler()->disableAudioPlaybackButtons();
751 }
752 
enableAudioPlaybackButtons()753 void MainWindow::enableAudioPlaybackButtons() { this->getToolMenuHandler()->enableAudioPlaybackButtons(); }
754 
setAudioPlaybackPaused(bool paused)755 void MainWindow::setAudioPlaybackPaused(bool paused) { this->getToolMenuHandler()->setAudioPlaybackPaused(paused); }
756 
loadMainCSS(GladeSearchpath * gladeSearchPath,const gchar * cssFilename)757 void MainWindow::loadMainCSS(GladeSearchpath* gladeSearchPath, const gchar* cssFilename) {
758     auto filepath = gladeSearchPath->findFile("", cssFilename);
759     GtkCssProvider* provider = gtk_css_provider_new();
760     gtk_css_provider_load_from_path(provider, filepath.u8string().c_str(), nullptr);
761     gtk_style_context_add_provider_for_screen(gdk_screen_get_default(), GTK_STYLE_PROVIDER(provider),
762                                               GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
763     g_object_unref(provider);
764 }
765