1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
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>
30 struct GtvApplicationWindowPrivate
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
;
49 #if __has_warning("-Wdeprecated-volatile")
50 #pragma clang diagnostic push
51 #pragma clang diagnostic ignored "-Wdeprecated-volatile"
54 G_DEFINE_TYPE_WITH_PRIVATE(GtvApplicationWindow
, gtv_application_window
, GTK_TYPE_APPLICATION_WINDOW
);
56 #if __has_warning("-Wdeprecated-volatile")
57 #pragma clang diagnostic pop
61 static GtvApplicationWindowPrivate
*
62 getPrivate(GtvApplicationWindow
* win
)
64 return static_cast<GtvApplicationWindowPrivate
*>(gtv_application_window_get_instance_private(win
));
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
);
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"));
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);
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();
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
);
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
147 static void initWindow(GtvApplicationWindow
* window
)
149 GtvApplicationWindowPrivate
* priv
= getPrivate(window
);
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)
158 GList
*focusChain
= nullptr;
159 focusChain
= g_list_append( focusChain
, window
->lokdocview
);
161 gtk_container_set_focus_chain ( GTK_CONTAINER (priv
->container
), focusChain
);
163 #pragma GCC diagnostic pop
164 #elif defined _MSC_VER
168 // TODO: Implement progressbar in statusbar
169 LibreOfficeKitDocument
* pDocument
= lok_doc_view_get_document(LOK_DOC_VIEW(window
->lokdocview
));
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
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
));
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
,
212 "Error occurred while opening the document: '%s'",
214 gtk_dialog_run(GTK_DIALOG(pDialog
));
215 gtk_widget_destroy(pDialog
);
218 gtk_widget_destroy(GTK_WIDGET(pDocView
));
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
);
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
),
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
);
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
,
329 #if GLIB_CHECK_VERSION(2,40,0)
330 g_assert_nonnull(window
->lokdocview
);
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);
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
);
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
;
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
,
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
));
405 gtv_application_window_get_main_toolbar(GtvApplicationWindow
* window
)
407 GtvApplicationWindowPrivate
* priv
= getPrivate(window
);
408 return GTV_MAIN_TOOLBAR(priv
->toolbarcontainer
);
412 gtv_application_window_set_toolbar_broadcast(GtvApplicationWindow
* window
, bool broadcast
)
414 GtvApplicationWindowPrivate
* priv
= getPrivate(window
);
415 priv
->toolbarBroadcast
= broadcast
;
419 gtv_application_window_get_toolbar_broadcast(GtvApplicationWindow
* window
)
421 GtvApplicationWindowPrivate
* priv
= getPrivate(window
);
422 return priv
->toolbarBroadcast
;
426 gtv_application_window_set_part_broadcast(GtvApplicationWindow
* window
, bool broadcast
)
428 GtvApplicationWindowPrivate
* priv
= getPrivate(window
);
429 priv
->partSelectorBroadcast
= broadcast
;
433 gtv_application_window_get_part_broadcast(GtvApplicationWindow
* window
)
435 GtvApplicationWindowPrivate
* priv
= getPrivate(window
);
436 return priv
->partSelectorBroadcast
;
440 gtv_application_window_register_child_window(GtvApplicationWindow
* window
, GtkWindow
* pChildWin
)
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
);
447 priv
->m_pChildWindows
= g_list_append(priv
->m_pChildWindows
, pChildWin
);
451 gtv_application_window_unregister_child_window(GtvApplicationWindow
* window
, GtkWindow
* pChildWin
)
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
);
459 priv
->m_pChildWindows
= g_list_remove(priv
->m_pChildWindows
, pChildWin
);
460 LibreOfficeKitDocument
* pDocument
= lok_doc_view_get_document(LOK_DOC_VIEW(window
->lokdocview
));
462 g_object_get(G_OBJECT(pChildWin
), "dialogid", &dialogId
, nullptr);
463 pDocument
->pClass
->postWindow(pDocument
, dialogId
, LOK_WINDOW_CLOSE
, nullptr);
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
)
477 g_object_get(G_OBJECT(pIt
->data
), "dialogid", &dialogId
, nullptr);
478 if (dialogId
== nWinId
)
480 ret
= GTK_WINDOW(pIt
->data
);
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
);
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
,
508 "width-request", 1024,
509 "height-request", 768,
510 "title", "LibreOffice GtkTiledViewer",
511 "window-position", GTK_WIN_POS_CENTER
,
512 "show-menubar", false,
516 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */