diff options
Diffstat (limited to 'libreofficekit/qa/gtktiledviewer/gtv-application-window.cxx')
-rw-r--r-- | libreofficekit/qa/gtktiledviewer/gtv-application-window.cxx | 509 |
1 files changed, 509 insertions, 0 deletions
diff --git a/libreofficekit/qa/gtktiledviewer/gtv-application-window.cxx b/libreofficekit/qa/gtktiledviewer/gtv-application-window.cxx new file mode 100644 index 000000000..4d2dbd41f --- /dev/null +++ b/libreofficekit/qa/gtktiledviewer/gtv-application-window.cxx @@ -0,0 +1,509 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <gtk/gtk.h> + +#include <memory> + +#include <LibreOfficeKit/LibreOfficeKitGtk.h> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> + +#include "gtv-application-window.hxx" +#include "gtv-main-toolbar.hxx" +#include "gtv-helpers.hxx" +#include "gtv-signal-handlers.hxx" +#include "gtv-lokdocview-signal-handlers.hxx" +#include "gtv-calc-header-bar.hxx" +#include "gtv-comments-sidebar.hxx" +#include "gtv-lok-dialog.hxx" + +#include <boost/property_tree/json_parser.hpp> + +namespace { + +struct GtvApplicationWindowPrivate +{ + GtkWidget* container; + GtkWidget* gridcontainer; + GtkWidget* toolbarcontainer; + GtkWidget* scrolledwindowcontainer; + + bool toolbarBroadcast; + bool partSelectorBroadcast; + + GList* m_pChildWindows; + + // Rendering args; options with which lokdocview was rendered in this window + GtvRenderingArgs* m_pRenderingArgs; +}; + +} + +#if defined __clang__ +#if __has_warning("-Wdeprecated-volatile") +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-volatile" +#endif +#endif +G_DEFINE_TYPE_WITH_PRIVATE(GtvApplicationWindow, gtv_application_window, GTK_TYPE_APPLICATION_WINDOW); +#if defined __clang__ +#if __has_warning("-Wdeprecated-volatile") +#pragma clang diagnostic pop +#endif +#endif + +static GtvApplicationWindowPrivate* +getPrivate(GtvApplicationWindow* win) +{ + return static_cast<GtvApplicationWindowPrivate*>(gtv_application_window_get_instance_private(win)); +} + +static void +gtv_application_window_init(GtvApplicationWindow* win) +{ + const std::string uiFilePath = GtvHelpers::getDirPath(__FILE__) + std::string(UI_FILE_NAME); + GtvGtkWrapper<GtkBuilder> builder(gtk_builder_new_from_file(uiFilePath.c_str()), + [](GtkBuilder* pBuilder) { + g_object_unref(pBuilder); + }); + GtvApplicationWindowPrivate* priv = getPrivate(win); + + // This is the parent GtkBox holding everything + priv->container = GTK_WIDGET(gtk_builder_get_object(builder.get(), "container")); + // Toolbar container + priv->toolbarcontainer = gtv_main_toolbar_new(); + + // Attach to the toolbar to main window + gtk_box_pack_start(GTK_BOX(priv->container), priv->toolbarcontainer, false, false, false); + gtk_box_reorder_child(GTK_BOX(priv->container), priv->toolbarcontainer, 0); + + priv->gridcontainer = GTK_WIDGET(gtk_builder_get_object(builder.get(), "maingrid")); + // scrolled window containing the main drawing area + win->scrolledwindow = GTK_WIDGET(gtk_builder_get_object(builder.get(), "scrolledwindow")); + // scrolledwindow container + priv->scrolledwindowcontainer = GTK_WIDGET(gtk_builder_get_object(builder.get(), "scrolledwindowcontainer")); + + GtkAdjustment* pHAdjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(win->scrolledwindow)); + g_signal_connect(pHAdjustment, "value-changed", G_CALLBACK(docAdjustmentChanged), win); + GtkAdjustment* pVAdjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(win->scrolledwindow)); + g_signal_connect(pVAdjustment, "value-changed", G_CALLBACK(docAdjustmentChanged), win); + + // calc header row bar + win->cornerarea = gtv_calc_header_bar_new(); + gtv_calc_header_bar_set_type_and_width(GTV_CALC_HEADER_BAR(win->cornerarea), CalcHeaderType::CORNER); + win->rowbar = gtv_calc_header_bar_new(); + gtv_calc_header_bar_set_type_and_width(GTV_CALC_HEADER_BAR(win->rowbar), CalcHeaderType::ROW); + win->columnbar = gtv_calc_header_bar_new(); + gtv_calc_header_bar_set_type_and_width(GTV_CALC_HEADER_BAR(win->columnbar), CalcHeaderType::COLUMN); + + // attach row/column/corner to the container + gtk_grid_attach(GTK_GRID(priv->gridcontainer), win->cornerarea, 0, 0, 1, 1); + gtk_grid_attach(GTK_GRID(priv->gridcontainer), win->rowbar, 0, 1, 1, 1); + gtk_grid_attach(GTK_GRID(priv->gridcontainer), win->columnbar, 1, 0, 1, 1); + + // statusbar + win->statusbar = GTK_WIDGET(gtk_builder_get_object(builder.get(), "statusbar")); + win->redlinelabel = GTK_WIDGET(gtk_builder_get_object(builder.get(), "redlinelabel")); + win->zoomlabel = GTK_WIDGET(gtk_builder_get_object(builder.get(), "zoomlabel")); + + win->findtoolbar = GTK_WIDGET(gtk_builder_get_object(builder.get(), "findtoolbar")); + win->findbarlabel = GTK_WIDGET(gtk_builder_get_object(builder.get(), "findbar_label")); + win->findbarEntry = GTK_WIDGET(gtk_builder_get_object(builder.get(), "findbar_entry")); + win->findAll = GTK_WIDGET(gtk_builder_get_object(builder.get(), "findbar_findall")); + priv->toolbarBroadcast = true; + priv->partSelectorBroadcast = true; + + gtk_container_add(GTK_CONTAINER(win), priv->container); + + priv->m_pChildWindows = nullptr; + priv->m_pRenderingArgs = new GtvRenderingArgs(); +} + +static void +gtv_application_window_dispose(GObject* object) +{ + GtvApplicationWindowPrivate* priv = getPrivate(GTV_APPLICATION_WINDOW(object)); + + delete priv->m_pRenderingArgs; + priv->m_pRenderingArgs = nullptr; + + G_OBJECT_CLASS (gtv_application_window_parent_class)->dispose (object); +} + +static void +gtv_application_window_class_init(GtvApplicationWindowClass* klass) +{ + G_OBJECT_CLASS(klass)->dispose = gtv_application_window_dispose; +} + +/// Helper function to do some tasks after widget is fully loaded (including +/// document load) +static void initWindow(GtvApplicationWindow* window) +{ + GtvApplicationWindowPrivate* priv = getPrivate(window); + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + GList *focusChain = nullptr; + focusChain = g_list_append( focusChain, window->lokdocview ); + + gtk_container_set_focus_chain ( GTK_CONTAINER (priv->container), focusChain ); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + + // TODO: Implement progressbar in statusbar + LibreOfficeKitDocument* pDocument = lok_doc_view_get_document(LOK_DOC_VIEW(window->lokdocview)); + if (pDocument) + { + LibreOfficeKitDocumentType eDocType = static_cast<LibreOfficeKitDocumentType>(pDocument->pClass->getDocumentType(pDocument)); + if (eDocType == LOK_DOCTYPE_SPREADSHEET) + { + // Align to top left corner, so the tiles are in sync with the + // row/column bar, even when zooming out enough that not all space is + // used. + gtk_widget_set_halign(GTK_WIDGET(window->lokdocview), GTK_ALIGN_START); + gtk_widget_set_valign(GTK_WIDGET(window->lokdocview), GTK_ALIGN_START); + } + + // By default make the document editable in a new window + lok_doc_view_set_edit(LOK_DOC_VIEW(window->lokdocview), true); + // Let toolbar adjust its button accordingly + gtv_main_toolbar_doc_loaded(GTV_MAIN_TOOLBAR(priv->toolbarcontainer), eDocType, true /* Edit button state */); + } + + // Fill our comments sidebar + gboolean bTiledAnnotations; + g_object_get(G_OBJECT(window->lokdocview), "tiled-annotations", &bTiledAnnotations, nullptr); + if (!bTiledAnnotations && pDocument) + { + window->commentssidebar = gtv_comments_sidebar_new(); + gtk_container_add(GTK_CONTAINER(priv->scrolledwindowcontainer), window->commentssidebar); + // fill the comments sidebar + gtv_comments_sidebar_view_annotations(GTV_COMMENTS_SIDEBAR(window->commentssidebar)); + } +} + +static void +gtv_application_open_document_callback(GObject* source_object, GAsyncResult* res, gpointer /*userdata*/) +{ + LOKDocView* pDocView = LOK_DOC_VIEW (source_object); + GtvApplicationWindow* window = GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView))); + GError* error = nullptr; + if (!lok_doc_view_open_document_finish(pDocView, res, &error)) + { + GtkWidget* pDialog = gtk_message_dialog_new(GTK_WINDOW(window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "Error occurred while opening the document: '%s'", + error->message); + gtk_dialog_run(GTK_DIALOG(pDialog)); + gtk_widget_destroy(pDialog); + + g_error_free(error); + gtk_widget_destroy(GTK_WIDGET(pDocView)); + gtk_main_quit(); + return; + } + + initWindow(window); +} + +/// Get the visible area of the scrolled window +void gtv_application_window_get_visible_area(GtvApplicationWindow* pWindow, GdkRectangle* pArea) +{ + GtkAdjustment* pHAdjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(pWindow->scrolledwindow)); + GtkAdjustment* pVAdjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(pWindow->scrolledwindow)); + + pArea->x = lok_doc_view_pixel_to_twip(LOK_DOC_VIEW(pWindow->lokdocview), + gtk_adjustment_get_value(pHAdjustment)); + pArea->y = lok_doc_view_pixel_to_twip(LOK_DOC_VIEW(pWindow->lokdocview), + gtk_adjustment_get_value(pVAdjustment)); + pArea->width = lok_doc_view_pixel_to_twip(LOK_DOC_VIEW(pWindow->lokdocview), + gtk_adjustment_get_page_size(pHAdjustment)); + pArea->height = lok_doc_view_pixel_to_twip(LOK_DOC_VIEW(pWindow->lokdocview), + gtk_adjustment_get_page_size(pVAdjustment)); +} + +void gtv_application_window_toggle_findbar(GtvApplicationWindow* window) +{ + if (gtk_widget_get_visible(window->findtoolbar)) + { + gtk_widget_hide(window->findtoolbar); + } + else + { + gtk_widget_show_all(window->findtoolbar); + gtk_widget_grab_focus(window->findtoolbar); + } +} + +GtkToolItem* gtv_application_window_find_tool_by_unocommand(GtvApplicationWindow* window, const std::string& unoCmd) +{ + GtvApplicationWindowPrivate* priv = getPrivate(window); + GtkToolItem* result = nullptr; + + // Find in the first toolbar + GtkContainer* pToolbar1 = gtv_main_toolbar_get_first_toolbar(GTV_MAIN_TOOLBAR(priv->toolbarcontainer)); + GtvGtkWrapper<GList> pList(gtk_container_get_children(pToolbar1), + [](GList* l) + { + g_list_free(l); + }); + for (GList* l = pList.get(); l != nullptr; l = l->next) + { + if (GTK_IS_TOOL_BUTTON(l->data)) + { + GtkToolButton* pButton = GTK_TOOL_BUTTON(l->data); + const gchar* pLabel = gtk_tool_button_get_label(pButton); + if (g_strcmp0(unoCmd.c_str(), pLabel) == 0) + { + result = GTK_TOOL_ITEM(pButton); + } + } + } + + // Look in second toolbar if not found + GtkContainer* pToolbar2 = gtv_main_toolbar_get_second_toolbar(GTV_MAIN_TOOLBAR(priv->toolbarcontainer)); + pList.reset(gtk_container_get_children(pToolbar2)); + for (GList* l = pList.get(); result == nullptr && l != nullptr; l = l->next) + { + if (GTK_IS_TOOL_BUTTON(l->data)) + { + GtkToolButton* pButton = GTK_TOOL_BUTTON(l->data); + const gchar* pLabel = gtk_tool_button_get_label(pButton); + if (g_strcmp0(unoCmd.c_str(), pLabel) == 0) + { + result = GTK_TOOL_ITEM(pButton); + } + } + } + + return result; +} + +static std::string +createRenderingArgsJSON(const GtvRenderingArgs* pRenderingArgs) +{ + boost::property_tree::ptree aTree; + if (pRenderingArgs->m_bHidePageShadow) + { + aTree.put(boost::property_tree::ptree::path_type(".uno:ShowBorderShadow/type", '/'), "boolean"); + aTree.put(boost::property_tree::ptree::path_type(".uno:ShowBorderShadow/value", '/'), false); + } + if (pRenderingArgs->m_bHideWhiteSpace) + { + aTree.put(boost::property_tree::ptree::path_type(".uno:HideWhitespace/type", '/'), "boolean"); + aTree.put(boost::property_tree::ptree::path_type(".uno:HideWhitespace/value", '/'), true); + } + aTree.put(boost::property_tree::ptree::path_type(".uno:Author/type", '/'), "string"); + aTree.put(boost::property_tree::ptree::path_type(".uno:Author/value", '/'), GtvHelpers::getNextAuthor()); + std::stringstream aStream; + boost::property_tree::write_json(aStream, aTree); + return aStream.str(); +} + +static void setupDocView(GtvApplicationWindow* window) +{ + GtvApplicationWindowPrivate* priv = getPrivate(window); + g_object_set(G_OBJECT(window->lokdocview), + "doc-password", true, + "doc-password-to-modify", true, + "tiled-annotations", priv->m_pRenderingArgs->m_bEnableTiledAnnotations, + nullptr); + +#if GLIB_CHECK_VERSION(2,40,0) + g_assert_nonnull(window->lokdocview); +#endif + g_signal_connect(window->lokdocview, "edit-changed", G_CALLBACK(LOKDocViewSigHandlers::editChanged), nullptr); + g_signal_connect(window->lokdocview, "command-changed", G_CALLBACK(LOKDocViewSigHandlers::commandChanged), nullptr); + g_signal_connect(window->lokdocview, "command-result", G_CALLBACK(LOKDocViewSigHandlers::commandResult), nullptr); + g_signal_connect(window->lokdocview, "search-not-found", G_CALLBACK(LOKDocViewSigHandlers::searchNotFound), nullptr); + g_signal_connect(window->lokdocview, "search-result-count", G_CALLBACK(LOKDocViewSigHandlers::searchResultCount), nullptr); + g_signal_connect(window->lokdocview, "part-changed", G_CALLBACK(LOKDocViewSigHandlers::partChanged), nullptr); + g_signal_connect(window->lokdocview, "hyperlink-clicked", G_CALLBACK(LOKDocViewSigHandlers::hyperlinkClicked), nullptr); + g_signal_connect(window->lokdocview, "cursor-changed", G_CALLBACK(LOKDocViewSigHandlers::cursorChanged), nullptr); + g_signal_connect(window->lokdocview, "address-changed", G_CALLBACK(LOKDocViewSigHandlers::addressChanged), nullptr); + g_signal_connect(window->lokdocview, "formula-changed", G_CALLBACK(LOKDocViewSigHandlers::formulaChanged), nullptr); + g_signal_connect(window->lokdocview, "password-required", G_CALLBACK(LOKDocViewSigHandlers::passwordRequired), nullptr); + g_signal_connect(window->lokdocview, "comment", G_CALLBACK(LOKDocViewSigHandlers::comment), nullptr); + g_signal_connect(window->lokdocview, "window", G_CALLBACK(LOKDocViewSigHandlers::window), window); + + g_signal_connect(window->lokdocview, "configure-event", G_CALLBACK(LOKDocViewSigHandlers::configureEvent), nullptr); +} + +void +gtv_application_window_create_view_from_window(GtvApplicationWindow* window) +{ + GtvApplicationWindowPrivate* priv = getPrivate(window); + GApplication* app = g_application_get_default(); + + GtvApplicationWindow* newWindow = GTV_APPLICATION_WINDOW(gtv_application_window_new(GTK_APPLICATION(app))); + const std::string aArguments = createRenderingArgsJSON(priv->m_pRenderingArgs); + newWindow->lokdocview = lok_doc_view_new_from_widget(LOK_DOC_VIEW(window->lokdocview), aArguments.c_str()); + setupDocView(newWindow); + + gtk_container_add(GTK_CONTAINER(newWindow->scrolledwindow), newWindow->lokdocview); + gtk_widget_show_all(newWindow->scrolledwindow); + gtk_window_present(GTK_WINDOW(newWindow)); + + initWindow(newWindow); +} + +void +gtv_application_window_load_document(GtvApplicationWindow* window, + const GtvRenderingArgs* aArgs, + const std::string& aDocPath) +{ + GtvApplicationWindowPrivate* priv = getPrivate(window); + // keep a copy of it; we need to use these for creating new views later + *(priv->m_pRenderingArgs) = *aArgs; + + // setup lokdocview + const char *pUserProfile = priv->m_pRenderingArgs->m_aUserProfile.empty() ? + nullptr : priv->m_pRenderingArgs->m_aUserProfile.c_str(); + + window->lokdocview = GTK_WIDGET( + g_initable_new(LOK_TYPE_DOC_VIEW, nullptr, nullptr, + "lopath", priv->m_pRenderingArgs->m_aLoPath.c_str(), + "unipoll", priv->m_pRenderingArgs->m_bUnipoll, + "userprofileurl", pUserProfile, + "halign", GTK_ALIGN_CENTER, + "valign", GTK_ALIGN_CENTER, + nullptr)); + + gtk_container_add(GTK_CONTAINER(window->scrolledwindow), window->lokdocview); + + setupDocView(window); + + // Create argument JSON + const std::string aArguments = createRenderingArgsJSON(priv->m_pRenderingArgs); + lok_doc_view_open_document(LOK_DOC_VIEW(window->lokdocview), aDocPath.c_str(), + aArguments.c_str(), nullptr, + gtv_application_open_document_callback, window->lokdocview); + + gtk_widget_show_all(GTK_WIDGET(window->scrolledwindow)); +} + +GtvMainToolbar* +gtv_application_window_get_main_toolbar(GtvApplicationWindow* window) +{ + GtvApplicationWindowPrivate* priv = getPrivate(window); + return GTV_MAIN_TOOLBAR(priv->toolbarcontainer); +} + +void +gtv_application_window_set_toolbar_broadcast(GtvApplicationWindow* window, bool broadcast) +{ + GtvApplicationWindowPrivate* priv = getPrivate(window); + priv->toolbarBroadcast = broadcast; +} + +gboolean +gtv_application_window_get_toolbar_broadcast(GtvApplicationWindow* window) +{ + GtvApplicationWindowPrivate* priv = getPrivate(window); + return priv->toolbarBroadcast; +} + +void +gtv_application_window_set_part_broadcast(GtvApplicationWindow* window, bool broadcast) +{ + GtvApplicationWindowPrivate* priv = getPrivate(window); + priv->partSelectorBroadcast = broadcast; +} + +gboolean +gtv_application_window_get_part_broadcast(GtvApplicationWindow* window) +{ + GtvApplicationWindowPrivate* priv = getPrivate(window); + return priv->partSelectorBroadcast; +} + +void +gtv_application_window_register_child_window(GtvApplicationWindow* window, GtkWindow* pChildWin) +{ + guint dialogid = 0; + g_object_get(G_OBJECT(pChildWin), "dialogid", &dialogid, nullptr); + g_debug("Register child window: dialogid [%d] in window[%p]", dialogid, window); + GtvApplicationWindowPrivate* priv = getPrivate(window); + if (pChildWin) + priv->m_pChildWindows = g_list_append(priv->m_pChildWindows, pChildWin); +} + +void +gtv_application_window_unregister_child_window(GtvApplicationWindow* window, GtkWindow* pChildWin) +{ + guint dialogid = 0; + g_object_get(G_OBJECT(pChildWin), "dialogid", &dialogid, nullptr); + g_debug("Unregister child window: dialogid [%d] in window[%p]", dialogid, window); + GtvApplicationWindowPrivate* priv = getPrivate(window); + if (pChildWin) + { + priv->m_pChildWindows = g_list_remove(priv->m_pChildWindows, pChildWin); + LibreOfficeKitDocument* pDocument = lok_doc_view_get_document(LOK_DOC_VIEW(window->lokdocview)); + guint dialogId = 0; + g_object_get(G_OBJECT(pChildWin), "dialogid", &dialogId, nullptr); + pDocument->pClass->postWindow(pDocument, dialogId, LOK_WINDOW_CLOSE, nullptr); + } +} + +GtkWindow* +gtv_application_window_get_child_window_by_id(GtvApplicationWindow* window, guint nWinId) +{ + GtvApplicationWindowPrivate* priv = getPrivate(window); + GList* pIt = nullptr; + GtkWindow* ret = nullptr; + // For now, only dialogs are registered as child window + for (pIt = priv->m_pChildWindows; pIt != nullptr; pIt = pIt->next) + { + guint dialogId = 0; + g_object_get(G_OBJECT(pIt->data), "dialogid", &dialogId, nullptr); + if (dialogId == nWinId) + { + ret = GTK_WINDOW(pIt->data); + break; + } + } + + return ret; +} + +GtkWidget* +gtv_application_window_get_parent(GtvApplicationWindow* window, guint nWinId) +{ + GtvApplicationWindowPrivate* priv = getPrivate(window); + GList* pIt = nullptr; + for (pIt = priv->m_pChildWindows; pIt != nullptr; pIt = pIt->next) + { + if (gtv_lok_dialog_is_parent_of(GTV_LOK_DIALOG(pIt->data), nWinId)) + return GTK_WIDGET(pIt->data); + } + return nullptr; +} + +GtvApplicationWindow* +gtv_application_window_new(GtkApplication* app) +{ + g_return_val_if_fail(GTK_IS_APPLICATION(app), nullptr); + + return GTV_APPLICATION_WINDOW(g_object_new(GTV_TYPE_APPLICATION_WINDOW, + "application", app, + "width-request", 1024, + "height-request", 768, + "title", "LibreOffice GtkTiledViewer", + "window-position", GTK_WIN_POS_CENTER, + "show-menubar", false, + nullptr)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |