LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / libreofficekit / qa / gtktiledviewer / gtv-application-window.cxx
blobef2f26917b30550089e08886091cfd5be71ece82
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <gtk/gtk.h>
12 #include <memory>
14 #include <LibreOfficeKit/LibreOfficeKitGtk.h>
15 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
17 #include "gtv-application-window.hxx"
18 #include "gtv-main-toolbar.hxx"
19 #include "gtv-helpers.hxx"
20 #include "gtv-signal-handlers.hxx"
21 #include "gtv-lokdocview-signal-handlers.hxx"
22 #include "gtv-calc-header-bar.hxx"
23 #include "gtv-comments-sidebar.hxx"
24 #include "gtv-lok-dialog.hxx"
26 #include <boost/property_tree/json_parser.hpp>
28 namespace {
30 struct GtvApplicationWindowPrivate
32 GtkWidget* container;
33 GtkWidget* gridcontainer;
34 GtkWidget* toolbarcontainer;
35 GtkWidget* scrolledwindowcontainer;
37 bool toolbarBroadcast;
38 bool partSelectorBroadcast;
40 GList* m_pChildWindows;
42 // Rendering args; options with which lokdocview was rendered in this window
43 GtvRenderingArgs* m_pRenderingArgs;
48 #if defined __clang__
49 #if __has_warning("-Wdeprecated-volatile")
50 #pragma clang diagnostic push
51 #pragma clang diagnostic ignored "-Wdeprecated-volatile"
52 #endif
53 #endif
54 G_DEFINE_TYPE_WITH_PRIVATE(GtvApplicationWindow, gtv_application_window, GTK_TYPE_APPLICATION_WINDOW);
55 #if defined __clang__
56 #if __has_warning("-Wdeprecated-volatile")
57 #pragma clang diagnostic pop
58 #endif
59 #endif
61 static GtvApplicationWindowPrivate*
62 getPrivate(GtvApplicationWindow* win)
64 return static_cast<GtvApplicationWindowPrivate*>(gtv_application_window_get_instance_private(win));
67 static void
68 gtv_application_window_init(GtvApplicationWindow* win)
70 const std::string uiFilePath = GtvHelpers::getDirPath(__FILE__) + std::string(UI_FILE_NAME);
71 GtvGtkWrapper<GtkBuilder> builder(gtk_builder_new_from_file(uiFilePath.c_str()),
72 [](GtkBuilder* pBuilder) {
73 g_object_unref(pBuilder);
74 });
75 GtvApplicationWindowPrivate* priv = getPrivate(win);
77 // This is the parent GtkBox holding everything
78 priv->container = GTK_WIDGET(gtk_builder_get_object(builder.get(), "container"));
79 // Toolbar container
80 priv->toolbarcontainer = gtv_main_toolbar_new();
82 // Attach to the toolbar to main window
83 gtk_box_pack_start(GTK_BOX(priv->container), priv->toolbarcontainer, false, false, false);
84 gtk_box_reorder_child(GTK_BOX(priv->container), priv->toolbarcontainer, 0);
86 priv->gridcontainer = GTK_WIDGET(gtk_builder_get_object(builder.get(), "maingrid"));
87 // scrolled window containing the main drawing area
88 win->scrolledwindow = GTK_WIDGET(gtk_builder_get_object(builder.get(), "scrolledwindow"));
89 // scrolledwindow container
90 priv->scrolledwindowcontainer = GTK_WIDGET(gtk_builder_get_object(builder.get(), "scrolledwindowcontainer"));
92 GtkAdjustment* pHAdjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(win->scrolledwindow));
93 g_signal_connect(pHAdjustment, "value-changed", G_CALLBACK(docAdjustmentChanged), win);
94 GtkAdjustment* pVAdjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(win->scrolledwindow));
95 g_signal_connect(pVAdjustment, "value-changed", G_CALLBACK(docAdjustmentChanged), win);
97 // calc header row bar
98 win->cornerarea = gtv_calc_header_bar_new();
99 gtv_calc_header_bar_set_type_and_width(GTV_CALC_HEADER_BAR(win->cornerarea), CalcHeaderType::CORNER);
100 win->rowbar = gtv_calc_header_bar_new();
101 gtv_calc_header_bar_set_type_and_width(GTV_CALC_HEADER_BAR(win->rowbar), CalcHeaderType::ROW);
102 win->columnbar = gtv_calc_header_bar_new();
103 gtv_calc_header_bar_set_type_and_width(GTV_CALC_HEADER_BAR(win->columnbar), CalcHeaderType::COLUMN);
105 // attach row/column/corner to the container
106 gtk_grid_attach(GTK_GRID(priv->gridcontainer), win->cornerarea, 0, 0, 1, 1);
107 gtk_grid_attach(GTK_GRID(priv->gridcontainer), win->rowbar, 0, 1, 1, 1);
108 gtk_grid_attach(GTK_GRID(priv->gridcontainer), win->columnbar, 1, 0, 1, 1);
110 // statusbar
111 win->statusbar = GTK_WIDGET(gtk_builder_get_object(builder.get(), "statusbar"));
112 win->redlinelabel = GTK_WIDGET(gtk_builder_get_object(builder.get(), "redlinelabel"));
113 win->zoomlabel = GTK_WIDGET(gtk_builder_get_object(builder.get(), "zoomlabel"));
115 win->findtoolbar = GTK_WIDGET(gtk_builder_get_object(builder.get(), "findtoolbar"));
116 win->findbarlabel = GTK_WIDGET(gtk_builder_get_object(builder.get(), "findbar_label"));
117 win->findbarEntry = GTK_WIDGET(gtk_builder_get_object(builder.get(), "findbar_entry"));
118 win->findAll = GTK_WIDGET(gtk_builder_get_object(builder.get(), "findbar_findall"));
119 priv->toolbarBroadcast = true;
120 priv->partSelectorBroadcast = true;
122 gtk_container_add(GTK_CONTAINER(win), priv->container);
124 priv->m_pChildWindows = nullptr;
125 priv->m_pRenderingArgs = new GtvRenderingArgs();
128 static void
129 gtv_application_window_dispose(GObject* object)
131 GtvApplicationWindowPrivate* priv = getPrivate(GTV_APPLICATION_WINDOW(object));
133 delete priv->m_pRenderingArgs;
134 priv->m_pRenderingArgs = nullptr;
136 G_OBJECT_CLASS (gtv_application_window_parent_class)->dispose (object);
139 static void
140 gtv_application_window_class_init(GtvApplicationWindowClass* klass)
142 G_OBJECT_CLASS(klass)->dispose = gtv_application_window_dispose;
145 /// Helper function to do some tasks after widget is fully loaded (including
146 /// document load)
147 static void initWindow(GtvApplicationWindow* window)
149 GtvApplicationWindowPrivate* priv = getPrivate(window);
151 #if defined __GNUC__
152 #pragma GCC diagnostic push
153 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
154 #elif defined _MSC_VER
155 #pragma warning(push)
156 #pragma warning(disable:4996)
157 #endif
158 GList *focusChain = nullptr;
159 focusChain = g_list_append( focusChain, window->lokdocview );
161 gtk_container_set_focus_chain ( GTK_CONTAINER (priv->container), focusChain );
162 #if defined __GNUC__
163 #pragma GCC diagnostic pop
164 #elif defined _MSC_VER
165 #pragma warning(pop)
166 #endif
168 // TODO: Implement progressbar in statusbar
169 LibreOfficeKitDocument* pDocument = lok_doc_view_get_document(LOK_DOC_VIEW(window->lokdocview));
170 if (pDocument)
172 LibreOfficeKitDocumentType eDocType = static_cast<LibreOfficeKitDocumentType>(pDocument->pClass->getDocumentType(pDocument));
173 if (eDocType == LOK_DOCTYPE_SPREADSHEET)
175 // Align to top left corner, so the tiles are in sync with the
176 // row/column bar, even when zooming out enough that not all space is
177 // used.
178 gtk_widget_set_halign(GTK_WIDGET(window->lokdocview), GTK_ALIGN_START);
179 gtk_widget_set_valign(GTK_WIDGET(window->lokdocview), GTK_ALIGN_START);
182 // By default make the document editable in a new window
183 lok_doc_view_set_edit(LOK_DOC_VIEW(window->lokdocview), true);
184 // Let toolbar adjust its button accordingly
185 gtv_main_toolbar_doc_loaded(GTV_MAIN_TOOLBAR(priv->toolbarcontainer), eDocType, true /* Edit button state */);
188 // Fill our comments sidebar
189 gboolean bTiledAnnotations;
190 g_object_get(G_OBJECT(window->lokdocview), "tiled-annotations", &bTiledAnnotations, nullptr);
191 if (!bTiledAnnotations && pDocument)
193 window->commentssidebar = gtv_comments_sidebar_new();
194 gtk_container_add(GTK_CONTAINER(priv->scrolledwindowcontainer), window->commentssidebar);
195 // fill the comments sidebar
196 gtv_comments_sidebar_view_annotations(GTV_COMMENTS_SIDEBAR(window->commentssidebar));
200 static void
201 gtv_application_open_document_callback(GObject* source_object, GAsyncResult* res, gpointer /*userdata*/)
203 LOKDocView* pDocView = LOK_DOC_VIEW (source_object);
204 GtvApplicationWindow* window = GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView)));
205 GError* error = nullptr;
206 if (!lok_doc_view_open_document_finish(pDocView, res, &error))
208 GtkWidget* pDialog = gtk_message_dialog_new(GTK_WINDOW(window),
209 GTK_DIALOG_DESTROY_WITH_PARENT,
210 GTK_MESSAGE_ERROR,
211 GTK_BUTTONS_CLOSE,
212 "Error occurred while opening the document: '%s'",
213 error->message);
214 gtk_dialog_run(GTK_DIALOG(pDialog));
215 gtk_widget_destroy(pDialog);
217 g_error_free(error);
218 gtk_widget_destroy(GTK_WIDGET(pDocView));
219 gtk_main_quit();
220 return;
223 initWindow(window);
226 /// Get the visible area of the scrolled window
227 void gtv_application_window_get_visible_area(GtvApplicationWindow* pWindow, GdkRectangle* pArea)
229 GtkAdjustment* pHAdjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(pWindow->scrolledwindow));
230 GtkAdjustment* pVAdjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(pWindow->scrolledwindow));
232 pArea->x = lok_doc_view_pixel_to_twip(LOK_DOC_VIEW(pWindow->lokdocview),
233 gtk_adjustment_get_value(pHAdjustment));
234 pArea->y = lok_doc_view_pixel_to_twip(LOK_DOC_VIEW(pWindow->lokdocview),
235 gtk_adjustment_get_value(pVAdjustment));
236 pArea->width = lok_doc_view_pixel_to_twip(LOK_DOC_VIEW(pWindow->lokdocview),
237 gtk_adjustment_get_page_size(pHAdjustment));
238 pArea->height = lok_doc_view_pixel_to_twip(LOK_DOC_VIEW(pWindow->lokdocview),
239 gtk_adjustment_get_page_size(pVAdjustment));
242 void gtv_application_window_toggle_findbar(GtvApplicationWindow* window)
244 if (gtk_widget_get_visible(window->findtoolbar))
246 gtk_widget_hide(window->findtoolbar);
248 else
250 gtk_widget_show_all(window->findtoolbar);
251 gtk_widget_grab_focus(window->findtoolbar);
255 GtkToolItem* gtv_application_window_find_tool_by_unocommand(GtvApplicationWindow* window, const std::string& unoCmd)
257 GtvApplicationWindowPrivate* priv = getPrivate(window);
258 GtkToolItem* result = nullptr;
260 // Find in the first toolbar
261 GtkContainer* pToolbar1 = gtv_main_toolbar_get_first_toolbar(GTV_MAIN_TOOLBAR(priv->toolbarcontainer));
262 GtvGtkWrapper<GList> pList(gtk_container_get_children(pToolbar1),
263 [](GList* l)
265 g_list_free(l);
267 for (GList* l = pList.get(); l != nullptr; l = l->next)
269 if (GTK_IS_TOOL_BUTTON(l->data))
271 GtkToolButton* pButton = GTK_TOOL_BUTTON(l->data);
272 const gchar* pLabel = gtk_tool_button_get_label(pButton);
273 if (g_strcmp0(unoCmd.c_str(), pLabel) == 0)
275 result = GTK_TOOL_ITEM(pButton);
280 // Look in second toolbar if not found
281 GtkContainer* pToolbar2 = gtv_main_toolbar_get_second_toolbar(GTV_MAIN_TOOLBAR(priv->toolbarcontainer));
282 pList.reset(gtk_container_get_children(pToolbar2));
283 for (GList* l = pList.get(); result == nullptr && l != nullptr; l = l->next)
285 if (GTK_IS_TOOL_BUTTON(l->data))
287 GtkToolButton* pButton = GTK_TOOL_BUTTON(l->data);
288 const gchar* pLabel = gtk_tool_button_get_label(pButton);
289 if (g_strcmp0(unoCmd.c_str(), pLabel) == 0)
291 result = GTK_TOOL_ITEM(pButton);
296 return result;
299 static std::string
300 createRenderingArgsJSON(const GtvRenderingArgs* pRenderingArgs)
302 boost::property_tree::ptree aTree;
303 if (pRenderingArgs->m_bHidePageShadow)
305 aTree.put(boost::property_tree::ptree::path_type(".uno:ShowBorderShadow/type", '/'), "boolean");
306 aTree.put(boost::property_tree::ptree::path_type(".uno:ShowBorderShadow/value", '/'), false);
308 if (pRenderingArgs->m_bHideWhiteSpace)
310 aTree.put(boost::property_tree::ptree::path_type(".uno:HideWhitespace/type", '/'), "boolean");
311 aTree.put(boost::property_tree::ptree::path_type(".uno:HideWhitespace/value", '/'), true);
313 aTree.put(boost::property_tree::ptree::path_type(".uno:Author/type", '/'), "string");
314 aTree.put(boost::property_tree::ptree::path_type(".uno:Author/value", '/'), GtvHelpers::getNextAuthor());
315 std::stringstream aStream;
316 boost::property_tree::write_json(aStream, aTree);
317 return aStream.str();
320 static void setupDocView(GtvApplicationWindow* window)
322 GtvApplicationWindowPrivate* priv = getPrivate(window);
323 g_object_set(G_OBJECT(window->lokdocview),
324 "doc-password", true,
325 "doc-password-to-modify", true,
326 "tiled-annotations", priv->m_pRenderingArgs->m_bEnableTiledAnnotations,
327 nullptr);
329 #if GLIB_CHECK_VERSION(2,40,0)
330 g_assert_nonnull(window->lokdocview);
331 #endif
332 g_signal_connect(window->lokdocview, "edit-changed", G_CALLBACK(LOKDocViewSigHandlers::editChanged), nullptr);
333 g_signal_connect(window->lokdocview, "command-changed", G_CALLBACK(LOKDocViewSigHandlers::commandChanged), nullptr);
334 g_signal_connect(window->lokdocview, "command-result", G_CALLBACK(LOKDocViewSigHandlers::commandResult), nullptr);
335 g_signal_connect(window->lokdocview, "search-not-found", G_CALLBACK(LOKDocViewSigHandlers::searchNotFound), nullptr);
336 g_signal_connect(window->lokdocview, "search-result-count", G_CALLBACK(LOKDocViewSigHandlers::searchResultCount), nullptr);
337 g_signal_connect(window->lokdocview, "part-changed", G_CALLBACK(LOKDocViewSigHandlers::partChanged), nullptr);
338 g_signal_connect(window->lokdocview, "hyperlink-clicked", G_CALLBACK(LOKDocViewSigHandlers::hyperlinkClicked), nullptr);
339 g_signal_connect(window->lokdocview, "content-control",
340 G_CALLBACK(LOKDocViewSigHandlers::contentControl), nullptr);
341 g_signal_connect(window->lokdocview, "cursor-changed", G_CALLBACK(LOKDocViewSigHandlers::cursorChanged), nullptr);
342 g_signal_connect(window->lokdocview, "address-changed", G_CALLBACK(LOKDocViewSigHandlers::addressChanged), nullptr);
343 g_signal_connect(window->lokdocview, "formula-changed", G_CALLBACK(LOKDocViewSigHandlers::formulaChanged), nullptr);
344 g_signal_connect(window->lokdocview, "password-required", G_CALLBACK(LOKDocViewSigHandlers::passwordRequired), nullptr);
345 g_signal_connect(window->lokdocview, "comment", G_CALLBACK(LOKDocViewSigHandlers::comment), nullptr);
346 g_signal_connect(window->lokdocview, "window", G_CALLBACK(LOKDocViewSigHandlers::window), window);
348 g_signal_connect(window->lokdocview, "configure-event", G_CALLBACK(LOKDocViewSigHandlers::configureEvent), nullptr);
351 void
352 gtv_application_window_create_view_from_window(GtvApplicationWindow* window)
354 GtvApplicationWindowPrivate* priv = getPrivate(window);
355 GApplication* app = g_application_get_default();
357 GtvApplicationWindow* newWindow = GTV_APPLICATION_WINDOW(gtv_application_window_new(GTK_APPLICATION(app)));
358 const std::string aArguments = createRenderingArgsJSON(priv->m_pRenderingArgs);
359 newWindow->lokdocview = lok_doc_view_new_from_widget(LOK_DOC_VIEW(window->lokdocview), aArguments.c_str());
360 setupDocView(newWindow);
362 gtk_container_add(GTK_CONTAINER(newWindow->scrolledwindow), newWindow->lokdocview);
363 gtk_widget_show_all(newWindow->scrolledwindow);
364 gtk_window_present(GTK_WINDOW(newWindow));
366 initWindow(newWindow);
369 void
370 gtv_application_window_load_document(GtvApplicationWindow* window,
371 const GtvRenderingArgs* aArgs,
372 const std::string& aDocPath)
374 GtvApplicationWindowPrivate* priv = getPrivate(window);
375 // keep a copy of it; we need to use these for creating new views later
376 *(priv->m_pRenderingArgs) = *aArgs;
378 // setup lokdocview
379 const char *pUserProfile = priv->m_pRenderingArgs->m_aUserProfile.empty() ?
380 nullptr : priv->m_pRenderingArgs->m_aUserProfile.c_str();
382 window->lokdocview = GTK_WIDGET(
383 g_initable_new(LOK_TYPE_DOC_VIEW, nullptr, nullptr,
384 "lopath", priv->m_pRenderingArgs->m_aLoPath.c_str(),
385 "unipoll", priv->m_pRenderingArgs->m_bUnipoll,
386 "userprofileurl", pUserProfile,
387 "halign", GTK_ALIGN_CENTER,
388 "valign", GTK_ALIGN_CENTER,
389 nullptr));
391 gtk_container_add(GTK_CONTAINER(window->scrolledwindow), window->lokdocview);
393 setupDocView(window);
395 // Create argument JSON
396 const std::string aArguments = createRenderingArgsJSON(priv->m_pRenderingArgs);
397 lok_doc_view_open_document(LOK_DOC_VIEW(window->lokdocview), aDocPath.c_str(),
398 aArguments.c_str(), nullptr,
399 gtv_application_open_document_callback, window->lokdocview);
401 gtk_widget_show_all(GTK_WIDGET(window->scrolledwindow));
404 GtvMainToolbar*
405 gtv_application_window_get_main_toolbar(GtvApplicationWindow* window)
407 GtvApplicationWindowPrivate* priv = getPrivate(window);
408 return GTV_MAIN_TOOLBAR(priv->toolbarcontainer);
411 void
412 gtv_application_window_set_toolbar_broadcast(GtvApplicationWindow* window, bool broadcast)
414 GtvApplicationWindowPrivate* priv = getPrivate(window);
415 priv->toolbarBroadcast = broadcast;
418 gboolean
419 gtv_application_window_get_toolbar_broadcast(GtvApplicationWindow* window)
421 GtvApplicationWindowPrivate* priv = getPrivate(window);
422 return priv->toolbarBroadcast;
425 void
426 gtv_application_window_set_part_broadcast(GtvApplicationWindow* window, bool broadcast)
428 GtvApplicationWindowPrivate* priv = getPrivate(window);
429 priv->partSelectorBroadcast = broadcast;
432 gboolean
433 gtv_application_window_get_part_broadcast(GtvApplicationWindow* window)
435 GtvApplicationWindowPrivate* priv = getPrivate(window);
436 return priv->partSelectorBroadcast;
439 void
440 gtv_application_window_register_child_window(GtvApplicationWindow* window, GtkWindow* pChildWin)
442 guint dialogid = 0;
443 g_object_get(G_OBJECT(pChildWin), "dialogid", &dialogid, nullptr);
444 g_debug("Register child window: dialogid [%d] in window[%p]", dialogid, window);
445 GtvApplicationWindowPrivate* priv = getPrivate(window);
446 if (pChildWin)
447 priv->m_pChildWindows = g_list_append(priv->m_pChildWindows, pChildWin);
450 void
451 gtv_application_window_unregister_child_window(GtvApplicationWindow* window, GtkWindow* pChildWin)
453 guint dialogid = 0;
454 g_object_get(G_OBJECT(pChildWin), "dialogid", &dialogid, nullptr);
455 g_debug("Unregister child window: dialogid [%d] in window[%p]", dialogid, window);
456 GtvApplicationWindowPrivate* priv = getPrivate(window);
457 if (pChildWin)
459 priv->m_pChildWindows = g_list_remove(priv->m_pChildWindows, pChildWin);
460 LibreOfficeKitDocument* pDocument = lok_doc_view_get_document(LOK_DOC_VIEW(window->lokdocview));
461 guint dialogId = 0;
462 g_object_get(G_OBJECT(pChildWin), "dialogid", &dialogId, nullptr);
463 pDocument->pClass->postWindow(pDocument, dialogId, LOK_WINDOW_CLOSE, nullptr);
467 GtkWindow*
468 gtv_application_window_get_child_window_by_id(GtvApplicationWindow* window, guint nWinId)
470 GtvApplicationWindowPrivate* priv = getPrivate(window);
471 GList* pIt = nullptr;
472 GtkWindow* ret = nullptr;
473 // For now, only dialogs are registered as child window
474 for (pIt = priv->m_pChildWindows; pIt != nullptr; pIt = pIt->next)
476 guint dialogId = 0;
477 g_object_get(G_OBJECT(pIt->data), "dialogid", &dialogId, nullptr);
478 if (dialogId == nWinId)
480 ret = GTK_WINDOW(pIt->data);
481 break;
485 return ret;
488 GtkWidget*
489 gtv_application_window_get_parent(GtvApplicationWindow* window, guint nWinId)
491 GtvApplicationWindowPrivate* priv = getPrivate(window);
492 GList* pIt = nullptr;
493 for (pIt = priv->m_pChildWindows; pIt != nullptr; pIt = pIt->next)
495 if (gtv_lok_dialog_is_parent_of(GTV_LOK_DIALOG(pIt->data), nWinId))
496 return GTK_WIDGET(pIt->data);
498 return nullptr;
501 GtvApplicationWindow*
502 gtv_application_window_new(GtkApplication* app)
504 g_return_val_if_fail(GTK_IS_APPLICATION(app), nullptr);
506 return GTV_APPLICATION_WINDOW(g_object_new(GTV_TYPE_APPLICATION_WINDOW,
507 "application", app,
508 "width-request", 1024,
509 "height-request", 768,
510 "title", "LibreOffice GtkTiledViewer",
511 "window-position", GTK_WIN_POS_CENTER,
512 "show-menubar", false,
513 nullptr));
516 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */