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/.
12 #include "gtv-application-window.hxx"
13 #include "gtv-helpers.hxx"
14 #include "gtv-calc-header-bar.hxx"
15 #include "gtv-comments-sidebar.hxx"
16 #include "gtv-lokdocview-signal-handlers.hxx"
17 #include "gtv-lok-dialog.hxx"
19 #include <boost/property_tree/json_parser.hpp>
21 static gboolean
deleteLokDialog(GtkWidget
* pWidget
, GdkEvent
* /*event*/, gpointer userdata
)
23 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(userdata
);
24 g_info("deleteLokDialog");
25 gtv_application_window_unregister_child_window(window
, GTK_WINDOW(pWidget
));
30 static gboolean
destroyLokDialog(GtkWidget
* pWidget
, gpointer userdata
)
32 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(userdata
);
33 g_info("destroyLokDialog");
34 gtv_application_window_unregister_child_window(window
, GTK_WINDOW(pWidget
));
39 void LOKDocViewSigHandlers::editChanged(LOKDocView
* pDocView
, gboolean bWasEdit
, gpointer
)
41 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView
)));
42 bool bEdit
= lok_doc_view_get_edit(LOK_DOC_VIEW(window
->lokdocview
));
43 g_info("signalEdit: %d -> %d", bWasEdit
, bEdit
);
45 // Let the main toolbar know, so that it can enable disable the button
46 GtvMainToolbar
* pMainToolbar
= gtv_application_window_get_main_toolbar(GTV_APPLICATION_WINDOW(window
));
47 gtv_main_toolbar_set_edit(pMainToolbar
, bEdit
);
50 void LOKDocViewSigHandlers::commandChanged(LOKDocView
* pDocView
, char* pPayload
, gpointer
)
52 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView
)));
53 std::string
aPayload(pPayload
);
54 size_t nPosition
= aPayload
.find('=');
55 if (nPosition
== std::string::npos
)
58 const std::string aKey
= aPayload
.substr(0, nPosition
);
59 const std::string aValue
= aPayload
.substr(nPosition
+ 1);
60 GtkToolItem
* pItem
= gtv_application_window_find_tool_by_unocommand(window
, aKey
);
63 if (aValue
== "true" || aValue
== "false") {
64 bool bEdit
= aValue
== "true";
65 if (bool(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(pItem
))) != bEdit
)
67 // Avoid invoking lok_doc_view_post_command().
68 // FIXME: maybe block/unblock the signal (see
69 // g_signal_handlers_block_by_func) ?
70 gtv_application_window_set_toolbar_broadcast(window
, false);
71 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(pItem
), bEdit
);
72 gtv_application_window_set_toolbar_broadcast(window
, true);
75 } else if (aValue
== "enabled" || aValue
== "disabled") {
76 bool bSensitive
= aValue
== "enabled";
77 gtk_widget_set_sensitive(GTK_WIDGET(pItem
), bSensitive
);
79 // Remember state, so in case edit is disable and enabled
80 // later, the correct sensitivity can be restored.
81 GtvMainToolbar
* pMainToolbar
= gtv_application_window_get_main_toolbar(window
);
82 gtv_main_toolbar_set_sensitive_internal(pMainToolbar
, pItem
, bSensitive
);
85 else if (aKey
== ".uno:TrackedChangeIndex")
87 std::string
aText("Current redline: ");
92 gtk_label_set_text(GTK_LABEL(window
->redlinelabel
), aText
.c_str());
96 void LOKDocViewSigHandlers::commandResult(LOKDocView
*, char* pPayload
, gpointer
)
98 fprintf(stderr
, "Command finished: %s\n", pPayload
);
101 void LOKDocViewSigHandlers::searchNotFound(LOKDocView
* pDocView
, char* , gpointer
)
103 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView
)));
104 gtk_label_set_text(GTK_LABEL(window
->findbarlabel
), "Search key not found");
107 void LOKDocViewSigHandlers::searchResultCount(LOKDocView
* pDocView
, char* pPayload
, gpointer
)
109 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView
)));
110 std::stringstream ss
;
111 ss
<< pPayload
<< " match(es)";
112 gtk_label_set_text(GTK_LABEL(window
->findbarlabel
), ss
.str().c_str());
115 void LOKDocViewSigHandlers::partChanged(LOKDocView
* /*pDocView*/, int, gpointer
)
117 // GtvApplicationWindow* window = GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView)));
118 //rWindow.m_bPartSelectorBroadcast = false;
119 // gtk_combo_box_set_active(GTK_COMBO_BOX(rWindow.m_pPartSelector), nPart);
120 // rWindow.m_bPartSelectorBroadcast = true;
123 void LOKDocViewSigHandlers::hyperlinkClicked(LOKDocView
* pDocView
, char* pPayload
, gpointer
)
125 GError
* pError
= nullptr;
126 #if GTK_CHECK_VERSION(3,22,0)
127 gtk_show_uri_on_window(
128 GTK_WINDOW (gtk_widget_get_toplevel(GTK_WIDGET(pDocView
))),
129 pPayload
, GDK_CURRENT_TIME
, &pError
);
132 gtk_show_uri(nullptr, pPayload
, GDK_CURRENT_TIME
, &pError
);
134 if (pError
!= nullptr)
136 g_warning("Unable to show URI %s : %s", pPayload
, pError
->message
);
137 g_error_free(pError
);
141 void LOKDocViewSigHandlers::cursorChanged(LOKDocView
* pDocView
, gint nX
, gint nY
,
142 gint
/*nWidth*/, gint
/*nHeight*/, gpointer
/*pData*/)
144 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView
)));
145 GtkAdjustment
* vadj
= gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(window
->scrolledwindow
));
146 GtkAdjustment
* hadj
= gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(window
->scrolledwindow
));
147 GdkRectangle visArea
;
151 gtv_application_window_get_visible_area(window
, &visArea
);
156 y
= nY
- visArea
.height
/2;
158 y
= gtk_adjustment_get_lower(vadj
);
160 else if (nY
> visArea
.y
+ visArea
.height
)
162 y
= nY
- visArea
.height
/2;
163 upper
= lok_doc_view_pixel_to_twip(pDocView
, gtk_adjustment_get_upper(vadj
));
171 x
= nX
- visArea
.width
/2;
173 x
= gtk_adjustment_get_lower(hadj
);
175 else if (nX
> visArea
.x
+ visArea
.width
)
177 x
= nX
- visArea
.width
/2;
178 upper
= lok_doc_view_pixel_to_twip(pDocView
, gtk_adjustment_get_upper(hadj
));
184 gtk_adjustment_set_value(vadj
, lok_doc_view_twip_to_pixel(pDocView
, y
));
186 gtk_adjustment_set_value(hadj
, lok_doc_view_twip_to_pixel(pDocView
, x
));
189 void LOKDocViewSigHandlers::addressChanged(LOKDocView
* pDocView
, char* pPayload
, gpointer
)
191 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView
)));
192 GtvMainToolbar
* toolbar
= gtv_application_window_get_main_toolbar(window
);
193 GtkEntry
* pAddressbar
= GTK_ENTRY(toolbar
->m_pAddressbar
);
194 gtk_entry_set_text(pAddressbar
, pPayload
);
197 void LOKDocViewSigHandlers::formulaChanged(LOKDocView
* pDocView
, char* pPayload
, gpointer
)
199 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView
)));
200 GtvMainToolbar
* toolbar
= gtv_application_window_get_main_toolbar(window
);
201 GtkEntry
* pFormulabar
= GTK_ENTRY(toolbar
->m_pFormulabar
);
202 gtk_entry_set_text(pFormulabar
, pPayload
);
205 void LOKDocViewSigHandlers::contentControl(LOKDocView
* pDocView
, gchar
* pJson
, gpointer
)
207 GtvApplicationWindow
* window
208 = GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView
)));
209 GtvMainToolbar
* toolbar
= gtv_application_window_get_main_toolbar(window
);
210 gtv_application_window_set_part_broadcast(window
, false);
211 gtk_list_store_clear(
212 GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(toolbar
->m_pContentControlSelector
))));
213 if (!window
->lokdocview
)
218 std::stringstream
aStream(pJson
);
219 boost::property_tree::ptree aTree
;
220 boost::property_tree::read_json(aStream
, aTree
);
221 boost::optional
<boost::property_tree::ptree
&> oItems
= aTree
.get_child_optional("items");
224 for (const auto& rItem
: *oItems
)
226 std::string aValue
= rItem
.second
.get_value
<std::string
>();
227 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(toolbar
->m_pContentControlSelector
),
232 boost::optional
<boost::property_tree::ptree
&> oDate
= aTree
.get_child_optional("date");
233 gtk_widget_set_sensitive(GTK_WIDGET(toolbar
->m_pContentControlDateSelector
), bool(oDate
));
235 gtv_application_window_set_part_broadcast(window
, true);
238 void LOKDocViewSigHandlers::passwordRequired(LOKDocView
* pDocView
, char* pUrl
, gboolean bModify
, gpointer
)
240 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView
)));
241 GtkWidget
* pPasswordDialog
= gtk_dialog_new_with_buttons ("Password required",
247 g_object_set(G_OBJECT(pPasswordDialog
), "resizable", FALSE
, nullptr);
248 GtkWidget
* pDialogMessageArea
= gtk_dialog_get_content_area (GTK_DIALOG (pPasswordDialog
));
249 GtkWidget
* pPasswordEntry
= gtk_entry_new ();
250 gtk_entry_set_visibility (GTK_ENTRY(pPasswordEntry
), FALSE
);
251 gtk_entry_set_invisible_char (GTK_ENTRY(pPasswordEntry
), '*');
252 gtk_box_pack_end(GTK_BOX(pDialogMessageArea
), pPasswordEntry
, true, true, 2);
255 GtkWidget
* pSecondaryLabel
= gtk_label_new ("Document requires password to edit");
256 gtk_box_pack_end(GTK_BOX(pDialogMessageArea
), pSecondaryLabel
, true, true, 2);
257 gtk_dialog_add_button (GTK_DIALOG (pPasswordDialog
), "Open as read-only", GTK_RESPONSE_ACCEPT
);
259 gtk_widget_show_all(pPasswordDialog
);
261 gint res
= gtk_dialog_run (GTK_DIALOG(pPasswordDialog
));
264 case GTK_RESPONSE_OK
:
265 lok_doc_view_set_document_password (LOK_DOC_VIEW(window
->lokdocview
), pUrl
, gtk_entry_get_text(GTK_ENTRY(pPasswordEntry
)));
267 case GTK_RESPONSE_ACCEPT
:
268 // User accepts to open this document as read-only
269 case GTK_RESPONSE_DELETE_EVENT
:
270 lok_doc_view_set_document_password (LOK_DOC_VIEW(window
->lokdocview
), pUrl
, nullptr);
274 gtk_widget_destroy(pPasswordDialog
);
277 void LOKDocViewSigHandlers::comment(LOKDocView
* pDocView
, gchar
* pComment
, gpointer
)
279 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView
)));
281 std::stringstream
aStream(pComment
);
282 boost::property_tree::ptree aRoot
;
283 boost::property_tree::read_json(aStream
, aRoot
);
284 boost::property_tree::ptree aComment
= aRoot
.get_child("comment");
285 GtvCommentsSidebar
* sidebar
= GTV_COMMENTS_SIDEBAR(window
->commentssidebar
);
286 GtkWidget
* pCommentsGrid
= sidebar
->commentsgrid
;
287 GtvGtkWrapper
<GList
> pChildren(gtk_container_get_children(GTK_CONTAINER(pCommentsGrid
)),
292 GtkWidget
* pSelf
= nullptr;
293 GtkWidget
* pParent
= nullptr;
294 for (GList
* l
= pChildren
.get(); l
!= nullptr; l
= l
->next
)
296 gchar
*id
= static_cast<gchar
*>(g_object_get_data(G_OBJECT(l
->data
), "id"));
298 if (g_strcmp0(id
, aComment
.get
<std::string
>("id").c_str()) == 0)
299 pSelf
= GTK_WIDGET(l
->data
);
301 // There is no 'parent' in Remove callbacks
302 if (g_strcmp0(id
, aComment
.get("parent", std::string("0")).c_str()) == 0)
303 pParent
= GTK_WIDGET(l
->data
);
306 if (aComment
.get
<std::string
>("action") == "Remove")
309 gtk_widget_destroy(pSelf
);
311 g_warning("Can't find the comment to remove in the list !!");
313 else if (aComment
.get
<std::string
>("action") == "Add" || aComment
.get
<std::string
>("action") == "Modify")
315 GtkWidget
* pCommentBox
= GtvHelpers::createCommentBox(aComment
);
316 if (pSelf
!= nullptr || pParent
!= nullptr)
318 gtk_grid_insert_next_to(GTK_GRID(pCommentsGrid
), pSelf
!= nullptr ? pSelf
: pParent
, GTK_POS_BOTTOM
);
319 gtk_grid_attach_next_to(GTK_GRID(pCommentsGrid
), pCommentBox
, pSelf
!= nullptr ? pSelf
: pParent
, GTK_POS_BOTTOM
, 1, 1);
322 gtk_container_add(GTK_CONTAINER(pCommentsGrid
), pCommentBox
);
324 gtk_widget_show_all(pCommentBox
);
326 // We added the widget already below the existing one, so destroy the
327 // already existing one now
329 gtk_widget_destroy(pSelf
);
333 void LOKDocViewSigHandlers::window(LOKDocView
* pDocView
, gchar
* pPayload
, gpointer pData
)
335 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(pData
);
337 std::stringstream
aStream(pPayload
);
338 boost::property_tree::ptree aRoot
;
339 boost::property_tree::read_json(aStream
, aRoot
);
340 const unsigned nWinId
= aRoot
.get
<unsigned>("id");
341 const std::string aAction
= aRoot
.get
<std::string
>("action");
343 if (aAction
== "created")
345 const std::string aType
= aRoot
.get
<std::string
>("type");
346 const std::string aSize
= aRoot
.get
<std::string
>("size");
347 const std::string aTitle
= aRoot
.get
<std::string
>("title", "");
348 std::vector
<int> aSizePoints
= GtvHelpers::split
<int>(aSize
, ", ", 2);
350 if (aType
== "dialog")
352 GtkWidget
* pDialog
= gtv_lok_dialog_new(pDocView
, nWinId
, aSizePoints
[0], aSizePoints
[1]);
353 g_info("created dialog, for dialogid: %d with size: %s", nWinId
, aSize
.c_str());
355 gtv_application_window_register_child_window(window
, GTK_WINDOW(pDialog
));
356 g_signal_connect(pDialog
, "destroy", G_CALLBACK(destroyLokDialog
), window
);
357 g_signal_connect(pDialog
, "delete-event", G_CALLBACK(deleteLokDialog
), window
);
360 gtk_window_set_title(GTK_WINDOW(pDialog
), aTitle
.c_str());
362 gtk_window_set_resizable(GTK_WINDOW(pDialog
), false);
363 gtk_widget_show_all(GTK_WIDGET(pDialog
));
364 gtk_window_present(GTK_WINDOW(pDialog
));
366 else if (aType
== "child")
368 const unsigned nParentId
= std::atoi(aRoot
.get
<std::string
>("parentId").c_str());
369 GtkWindow
* pDialog
= gtv_application_window_get_child_window_by_id(window
, nParentId
);
372 g_warning("no parent dialog (id: %d) for created child; is child a sidebar?", nParentId
);
375 const std::string aPos
= aRoot
.get
<std::string
>("position");
376 std::vector
<int> aPosPoints
= GtvHelpers::split
<int>(aPos
, ", ", 2);
377 gtv_lok_dialog_child_create(GTV_LOK_DIALOG(pDialog
), nWinId
, aPosPoints
[0], aPosPoints
[1], aSizePoints
[0], aSizePoints
[1]);
382 // check if it's a child window
383 GtkWidget
* pParent
= gtv_application_window_get_parent(window
, nWinId
);
384 if (pParent
) // it's a floating window in the dialog
386 if (aAction
== "invalidate")
387 gtv_lok_dialog_child_invalidate(GTV_LOK_DIALOG(pParent
));
388 else if (aAction
== "close")
389 gtv_lok_dialog_child_close(GTV_LOK_DIALOG(pParent
));
391 else if (GtkWindow
* pDialog
= gtv_application_window_get_child_window_by_id(window
, nWinId
))
392 { // it's the dialog window itself
393 if (aAction
== "close")
394 gtk_widget_destroy(GTK_WIDGET(pDialog
));
395 else if (aAction
== "size_changed")
397 const std::string aSize
= aRoot
.get
<std::string
>("size");
398 std::vector
<int> aSizePoints
= GtvHelpers::split
<int>(aSize
, ", ", 2);
399 if (aSizePoints
.size() != 2)
401 g_error("Malformed size_changed callback");
405 g_object_set(G_OBJECT(pDialog
),
406 "width", aSizePoints
[0],
407 "height", aSizePoints
[1],
410 GdkRectangle aGdkRectangle
= {0, 0, 0, 0};
411 gtv_lok_dialog_invalidate(GTV_LOK_DIALOG(pDialog
), aGdkRectangle
);
413 else if (aAction
== "invalidate")
415 GdkRectangle aGdkRectangle
= {0, 0, 0, 0};
418 const std::string aRectangle
= aRoot
.get
<std::string
>("rectangle");
419 std::vector
<int> aRectPoints
= GtvHelpers::split
<int>(aRectangle
, ", ", 4);
420 if (aRectPoints
.size() == 4)
421 aGdkRectangle
= {aRectPoints
[0], aRectPoints
[1], aRectPoints
[2], aRectPoints
[3]};
423 catch(const std::exception
&)
426 gtv_lok_dialog_invalidate(GTV_LOK_DIALOG(pDialog
), aGdkRectangle
);
428 else if (aAction
== "title_changed")
430 const std::string aTitle
= aRoot
.get
<std::string
>("title", "");
431 gtk_window_set_title(pDialog
, aTitle
.c_str());
437 gboolean
LOKDocViewSigHandlers::configureEvent(GtkWidget
* pWidget
, GdkEventConfigure
* /*pEvent*/, gpointer
/*pData*/)
439 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pWidget
)));
441 gboolean isInit
= false;
442 g_object_get(G_OBJECT(window
->lokdocview
), "is-initialized", &isInit
, nullptr);
445 g_info("Ignoring configure event; document not yet ready");
449 LibreOfficeKitDocument
* pDocument
= lok_doc_view_get_document(LOK_DOC_VIEW(window
->lokdocview
));
450 if (!pDocument
|| pDocument
->pClass
->getDocumentType(pDocument
) != LOK_DOCTYPE_SPREADSHEET
)
453 GtkAdjustment
* pVAdjustment
= gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(window
->scrolledwindow
));
454 int rowSizePixel
= GTV_CALC_HEADER_BAR(window
->rowbar
)->m_nSizePixel
= gtk_adjustment_get_page_size(pVAdjustment
);
455 int rowPosPixel
= GTV_CALC_HEADER_BAR(window
->rowbar
)->m_nPositionPixel
= gtk_adjustment_get_value(pVAdjustment
);
456 GtkAdjustment
* pHAdjustment
= gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(window
->scrolledwindow
));
457 int colSizePixel
= GTV_CALC_HEADER_BAR(window
->columnbar
)->m_nSizePixel
= gtk_adjustment_get_page_size(pHAdjustment
);
458 int colPosPixel
= GTV_CALC_HEADER_BAR(window
->columnbar
)->m_nPositionPixel
= gtk_adjustment_get_value(pHAdjustment
);
460 std::stringstream aCommand
;
461 aCommand
<< ".uno:ViewRowColumnHeaders";
462 aCommand
<< "?x=" << int(lok_doc_view_pixel_to_twip(LOK_DOC_VIEW(window
->lokdocview
), colPosPixel
));
463 aCommand
<< "&width=" << int(lok_doc_view_pixel_to_twip(LOK_DOC_VIEW(window
->lokdocview
), colSizePixel
));
464 aCommand
<< "&y=" << int(lok_doc_view_pixel_to_twip(LOK_DOC_VIEW(window
->lokdocview
), rowPosPixel
));
465 aCommand
<< "&height=" << int(lok_doc_view_pixel_to_twip(LOK_DOC_VIEW(window
->lokdocview
), rowSizePixel
));
466 std::stringstream ss
;
467 ss
<< "lok::Document::getCommandValues(" << aCommand
.str() << ")";
468 g_info("%s", ss
.str().c_str());
469 char* pValues
= pDocument
->pClass
->getCommandValues(pDocument
, aCommand
.str().c_str());
470 g_info("lok::Document::getCommandValues() returned '%s'", pValues
);
471 std::stringstream
aStream(pValues
);
473 assert(!aStream
.str().empty());
474 boost::property_tree::ptree aTree
;
475 boost::property_tree::read_json(aStream
, aTree
);
477 gtv_calc_header_bar_configure(GTV_CALC_HEADER_BAR(window
->rowbar
), &aTree
.get_child("rows"));
478 gtv_calc_header_bar_configure(GTV_CALC_HEADER_BAR(window
->columnbar
), &aTree
.get_child("columns"));
479 gtv_calc_header_bar_configure(GTV_CALC_HEADER_BAR(window
->cornerarea
), nullptr);
484 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */