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-signal-handlers.hxx"
15 #include "gtv-calc-header-bar.hxx"
16 #include "gtv-comments-sidebar.hxx"
17 #include "gtv-lokdocview-signal-handlers.hxx"
18 #include "gtv-lok-dialog.hxx"
20 #include <boost/property_tree/json_parser.hpp>
21 #include <boost/optional.hpp>
25 static gboolean
deleteLokDialog(GtkWidget
* pWidget
, GdkEvent
* /*event*/, gpointer userdata
)
27 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(userdata
);
28 g_info("deleteLokDialog");
29 gtv_application_window_unregister_child_window(window
, GTK_WINDOW(pWidget
));
34 static gboolean
destroyLokDialog(GtkWidget
* pWidget
, gpointer userdata
)
36 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(userdata
);
37 g_info("destroyLokDialog");
38 gtv_application_window_unregister_child_window(window
, GTK_WINDOW(pWidget
));
43 void LOKDocViewSigHandlers::editChanged(LOKDocView
* pDocView
, gboolean bWasEdit
, gpointer
)
45 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView
)));
46 bool bEdit
= lok_doc_view_get_edit(LOK_DOC_VIEW(window
->lokdocview
));
47 g_info("signalEdit: %d -> %d", bWasEdit
, bEdit
);
49 // Let the main toolbar know, so that it can enable disable the button
50 GtvMainToolbar
* pMainToolbar
= gtv_application_window_get_main_toolbar(GTV_APPLICATION_WINDOW(window
));
51 gtv_main_toolbar_set_edit(pMainToolbar
, bEdit
);
54 void LOKDocViewSigHandlers::commandChanged(LOKDocView
* pDocView
, char* pPayload
, gpointer
)
56 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView
)));
57 std::string
aPayload(pPayload
);
58 size_t nPosition
= aPayload
.find('=');
59 if (nPosition
!= std::string::npos
)
61 const std::string aKey
= aPayload
.substr(0, nPosition
);
62 const std::string aValue
= aPayload
.substr(nPosition
+ 1);
63 GtkToolItem
* pItem
= gtv_application_window_find_tool_by_unocommand(window
, aKey
);
66 if (aValue
== "true" || aValue
== "false") {
67 bool bEdit
= aValue
== "true";
68 if (bool(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(pItem
))) != bEdit
)
70 // Avoid invoking lok_doc_view_post_command().
71 // FIXME: maybe block/unblock the signal (see
72 // g_signal_handlers_block_by_func) ?
73 gtv_application_window_set_toolbar_broadcast(window
, false);
74 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(pItem
), bEdit
);
75 gtv_application_window_set_toolbar_broadcast(window
, true);
78 } else if (aValue
== "enabled" || aValue
== "disabled") {
79 bool bSensitive
= aValue
== "enabled";
80 gtk_widget_set_sensitive(GTK_WIDGET(pItem
), bSensitive
);
82 // Remember state, so in case edit is disable and enabled
83 // later, the correct sensitivity can be restored.
84 GtvMainToolbar
* pMainToolbar
= gtv_application_window_get_main_toolbar(window
);
85 gtv_main_toolbar_set_sensitive_internal(pMainToolbar
, pItem
, bSensitive
);
88 else if (aKey
== ".uno:TrackedChangeIndex")
90 std::string
aText("Current redline: ");
95 gtk_label_set_text(GTK_LABEL(window
->redlinelabel
), aText
.c_str());
100 void LOKDocViewSigHandlers::commandResult(LOKDocView
*, char* pPayload
, gpointer
)
102 fprintf(stderr
, "Command finished: %s\n", pPayload
);
105 void LOKDocViewSigHandlers::searchNotFound(LOKDocView
* pDocView
, char* , gpointer
)
107 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView
)));
108 gtk_label_set_text(GTK_LABEL(window
->findbarlabel
), "Search key not found");
111 void LOKDocViewSigHandlers::searchResultCount(LOKDocView
* pDocView
, char* pPayload
, gpointer
)
113 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView
)));
114 std::stringstream ss
;
115 ss
<< pPayload
<< " match(es)";
116 gtk_label_set_text(GTK_LABEL(window
->findbarlabel
), ss
.str().c_str());
119 void LOKDocViewSigHandlers::partChanged(LOKDocView
* /*pDocView*/, int, gpointer
)
121 // GtvApplicationWindow* window = GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView)));
122 //rWindow.m_bPartSelectorBroadcast = false;
123 // gtk_combo_box_set_active(GTK_COMBO_BOX(rWindow.m_pPartSelector), nPart);
124 // rWindow.m_bPartSelectorBroadcast = true;
127 void LOKDocViewSigHandlers::hyperlinkClicked(LOKDocView
* pDocView
, char* pPayload
, gpointer
)
129 GError
* pError
= nullptr;
130 #if GTK_CHECK_VERSION(3,22,0)
131 gtk_show_uri_on_window(
132 GTK_WINDOW (gtk_widget_get_toplevel(GTK_WIDGET(pDocView
))),
133 pPayload
, GDK_CURRENT_TIME
, &pError
);
136 gtk_show_uri(nullptr, pPayload
, GDK_CURRENT_TIME
, &pError
);
138 if (pError
!= nullptr)
140 g_warning("Unable to show URI %s : %s", pPayload
, pError
->message
);
141 g_error_free(pError
);
145 void LOKDocViewSigHandlers::cursorChanged(LOKDocView
* pDocView
, gint nX
, gint nY
,
146 gint
/*nWidth*/, gint
/*nHeight*/, gpointer
/*pData*/)
148 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView
)));
149 GtkAdjustment
* vadj
= gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(window
->scrolledwindow
));
150 GtkAdjustment
* hadj
= gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(window
->scrolledwindow
));
151 GdkRectangle visArea
;
155 gtv_application_window_get_visible_area(window
, &visArea
);
160 y
= nY
- visArea
.height
/2;
162 y
= gtk_adjustment_get_lower(vadj
);
164 else if (nY
> visArea
.y
+ visArea
.height
)
166 y
= nY
- visArea
.height
/2;
167 upper
= lok_doc_view_pixel_to_twip(pDocView
, gtk_adjustment_get_upper(vadj
));
175 x
= nX
- visArea
.width
/2;
177 x
= gtk_adjustment_get_lower(hadj
);
179 else if (nX
> visArea
.x
+ visArea
.width
)
181 x
= nX
- visArea
.width
/2;
182 upper
= lok_doc_view_pixel_to_twip(pDocView
, gtk_adjustment_get_upper(hadj
));
188 gtk_adjustment_set_value(vadj
, lok_doc_view_twip_to_pixel(pDocView
, y
));
190 gtk_adjustment_set_value(hadj
, lok_doc_view_twip_to_pixel(pDocView
, x
));
193 void LOKDocViewSigHandlers::addressChanged(LOKDocView
* pDocView
, char* pPayload
, gpointer
)
195 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView
)));
196 GtvMainToolbar
* toolbar
= gtv_application_window_get_main_toolbar(window
);
197 GtkEntry
* pAddressbar
= GTK_ENTRY(toolbar
->m_pAddressbar
);
198 gtk_entry_set_text(pAddressbar
, pPayload
);
201 void LOKDocViewSigHandlers::formulaChanged(LOKDocView
* pDocView
, char* pPayload
, gpointer
)
203 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView
)));
204 GtvMainToolbar
* toolbar
= gtv_application_window_get_main_toolbar(window
);
205 GtkEntry
* pFormulabar
= GTK_ENTRY(toolbar
->m_pFormulabar
);
206 gtk_entry_set_text(pFormulabar
, pPayload
);
209 void LOKDocViewSigHandlers::passwordRequired(LOKDocView
* pDocView
, char* pUrl
, gboolean bModify
, gpointer
)
211 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView
)));
212 GtkWidget
* pPasswordDialog
= gtk_dialog_new_with_buttons ("Password required",
218 g_object_set(G_OBJECT(pPasswordDialog
), "resizable", FALSE
, nullptr);
219 GtkWidget
* pDialogMessageArea
= gtk_dialog_get_content_area (GTK_DIALOG (pPasswordDialog
));
220 GtkWidget
* pPasswordEntry
= gtk_entry_new ();
221 gtk_entry_set_visibility (GTK_ENTRY(pPasswordEntry
), FALSE
);
222 gtk_entry_set_invisible_char (GTK_ENTRY(pPasswordEntry
), '*');
223 gtk_box_pack_end(GTK_BOX(pDialogMessageArea
), pPasswordEntry
, TRUE
, TRUE
, 2);
226 GtkWidget
* pSecondaryLabel
= gtk_label_new ("Document requires password to edit");
227 gtk_box_pack_end(GTK_BOX(pDialogMessageArea
), pSecondaryLabel
, TRUE
, TRUE
, 2);
228 gtk_dialog_add_button (GTK_DIALOG (pPasswordDialog
), "Open as read-only", GTK_RESPONSE_ACCEPT
);
230 gtk_widget_show_all(pPasswordDialog
);
232 gint res
= gtk_dialog_run (GTK_DIALOG(pPasswordDialog
));
235 case GTK_RESPONSE_OK
:
236 lok_doc_view_set_document_password (LOK_DOC_VIEW(window
->lokdocview
), pUrl
, gtk_entry_get_text(GTK_ENTRY(pPasswordEntry
)));
238 case GTK_RESPONSE_ACCEPT
:
239 // User accepts to open this document as read-only
240 case GTK_RESPONSE_DELETE_EVENT
:
241 lok_doc_view_set_document_password (LOK_DOC_VIEW(window
->lokdocview
), pUrl
, nullptr);
245 gtk_widget_destroy(pPasswordDialog
);
248 void LOKDocViewSigHandlers::comment(LOKDocView
* pDocView
, gchar
* pComment
, gpointer
)
250 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView
)));
252 std::stringstream
aStream(pComment
);
253 boost::property_tree::ptree aRoot
;
254 boost::property_tree::read_json(aStream
, aRoot
);
255 boost::property_tree::ptree aComment
= aRoot
.get_child("comment");
256 GtvCommentsSidebar
* sidebar
= GTV_COMMENTS_SIDEBAR(window
->commentssidebar
);
257 GtkWidget
* pCommentsGrid
= sidebar
->commentsgrid
;
258 GtvGtkWrapper
<GList
> pChildren(gtk_container_get_children(GTK_CONTAINER(pCommentsGrid
)),
263 GtkWidget
* pSelf
= nullptr;
264 GtkWidget
* pParent
= nullptr;
265 for (GList
* l
= pChildren
.get(); l
!= nullptr; l
= l
->next
)
267 gchar
*id
= static_cast<gchar
*>(g_object_get_data(G_OBJECT(l
->data
), "id"));
269 if (g_strcmp0(id
, aComment
.get
<std::string
>("id").c_str()) == 0)
270 pSelf
= GTK_WIDGET(l
->data
);
272 // There is no 'parent' in Remove callbacks
273 if (g_strcmp0(id
, aComment
.get("parent", std::string("0")).c_str()) == 0)
274 pParent
= GTK_WIDGET(l
->data
);
277 if (aComment
.get
<std::string
>("action") == "Remove")
280 gtk_widget_destroy(pSelf
);
282 g_warning("Can't find the comment to remove in the list !!");
284 else if (aComment
.get
<std::string
>("action") == "Add" || aComment
.get
<std::string
>("action") == "Modify")
286 GtkWidget
* pCommentBox
= GtvHelpers::createCommentBox(aComment
);
287 if (pSelf
!= nullptr || pParent
!= nullptr)
289 gtk_grid_insert_next_to(GTK_GRID(pCommentsGrid
), pSelf
!= nullptr ? pSelf
: pParent
, GTK_POS_BOTTOM
);
290 gtk_grid_attach_next_to(GTK_GRID(pCommentsGrid
), pCommentBox
, pSelf
!= nullptr ? pSelf
: pParent
, GTK_POS_BOTTOM
, 1, 1);
293 gtk_container_add(GTK_CONTAINER(pCommentsGrid
), pCommentBox
);
295 gtk_widget_show_all(pCommentBox
);
297 // We added the widget already below the existing one, so destroy the
298 // already existing one now
300 gtk_widget_destroy(pSelf
);
304 void LOKDocViewSigHandlers::window(LOKDocView
* pDocView
, gchar
* pPayload
, gpointer pData
)
306 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(pData
);
308 std::stringstream
aStream(pPayload
);
309 boost::property_tree::ptree aRoot
;
310 boost::property_tree::read_json(aStream
, aRoot
);
311 const unsigned nWinId
= aRoot
.get
<unsigned>("id");
312 const std::string aAction
= aRoot
.get
<std::string
>("action");
314 if (aAction
== "created")
316 const std::string aType
= aRoot
.get
<std::string
>("type");
317 const std::string aSize
= aRoot
.get
<std::string
>("size");
318 const std::string aTitle
= aRoot
.get
<std::string
>("title", "");
319 std::vector
<int> aSizePoints
= GtvHelpers::split
<int>(aSize
, ", ", 2);
321 if (aType
== "dialog")
323 GtkWidget
* pDialog
= gtv_lok_dialog_new(pDocView
, nWinId
, aSizePoints
[0], aSizePoints
[1]);
324 g_info("created dialog, for dialogid: %d with size: %s", nWinId
, aSize
.c_str());
326 gtv_application_window_register_child_window(window
, GTK_WINDOW(pDialog
));
327 g_signal_connect(pDialog
, "destroy", G_CALLBACK(destroyLokDialog
), window
);
328 g_signal_connect(pDialog
, "delete-event", G_CALLBACK(deleteLokDialog
), window
);
331 gtk_window_set_title(GTK_WINDOW(pDialog
), aTitle
.c_str());
333 gtk_window_set_resizable(GTK_WINDOW(pDialog
), false);
334 gtk_widget_show_all(GTK_WIDGET(pDialog
));
335 gtk_window_present(GTK_WINDOW(pDialog
));
337 else if (aType
== "child")
339 const unsigned nParentId
= std::atoi(aRoot
.get
<std::string
>("parentId").c_str());
340 GtkWindow
* pDialog
= gtv_application_window_get_child_window_by_id(window
, nParentId
);
341 const std::string aPos
= aRoot
.get
<std::string
>("position");
342 std::vector
<int> aPosPoints
= GtvHelpers::split
<int>(aPos
, ", ", 2);
343 gtv_lok_dialog_child_create(GTV_LOK_DIALOG(pDialog
), nWinId
, aPosPoints
[0], aPosPoints
[1], aSizePoints
[0], aSizePoints
[1]);
348 // check if it's a child window
349 GtkWidget
* pParent
= gtv_application_window_get_parent(window
, nWinId
);
350 if (pParent
) // it's a floating window in the dialog
352 if (aAction
== "invalidate")
353 gtv_lok_dialog_child_invalidate(GTV_LOK_DIALOG(pParent
));
354 else if (aAction
== "close")
355 gtv_lok_dialog_child_close(GTV_LOK_DIALOG(pParent
));
357 else if (GtkWindow
* pDialog
= gtv_application_window_get_child_window_by_id(window
, nWinId
))
358 { // it's the dialog window itself
359 if (aAction
== "close")
360 gtk_widget_destroy(GTK_WIDGET(pDialog
));
361 else if (aAction
== "size_changed")
363 const std::string aSize
= aRoot
.get
<std::string
>("size");
364 std::vector
<int> aSizePoints
= GtvHelpers::split
<int>(aSize
, ", ", 2);
365 if (aSizePoints
.size() != 2)
367 g_error("Malformed size_changed callback");
371 g_object_set(G_OBJECT(pDialog
),
372 "width", aSizePoints
[0],
373 "height", aSizePoints
[1],
376 GdkRectangle aGdkRectangle
= {0, 0, 0, 0};
377 gtv_lok_dialog_invalidate(GTV_LOK_DIALOG(pDialog
), aGdkRectangle
);
379 else if (aAction
== "invalidate")
381 GdkRectangle aGdkRectangle
= {0, 0, 0, 0};
384 const std::string aRectangle
= aRoot
.get
<std::string
>("rectangle");
385 std::vector
<int> aRectPoints
= GtvHelpers::split
<int>(aRectangle
, ", ", 4);
386 if (aRectPoints
.size() == 4)
387 aGdkRectangle
= {aRectPoints
[0], aRectPoints
[1], aRectPoints
[2], aRectPoints
[3]};
389 catch(const std::exception
&)
392 gtv_lok_dialog_invalidate(GTV_LOK_DIALOG(pDialog
), aGdkRectangle
);
394 else if (aAction
== "title_changed")
396 const std::string aTitle
= aRoot
.get
<std::string
>("title", "");
397 gtk_window_set_title(pDialog
, aTitle
.c_str());
403 gboolean
LOKDocViewSigHandlers::configureEvent(GtkWidget
* pWidget
, GdkEventConfigure
* /*pEvent*/, gpointer
/*pData*/)
405 GtvApplicationWindow
* window
= GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pWidget
)));
407 gboolean isInit
= false;
408 g_object_get(G_OBJECT(window
->lokdocview
), "is-initialized", &isInit
, nullptr);
411 g_info("Ignoring configure event; document not yet ready");
415 LibreOfficeKitDocument
* pDocument
= lok_doc_view_get_document(LOK_DOC_VIEW(window
->lokdocview
));
416 if (pDocument
&& pDocument
->pClass
->getDocumentType(pDocument
) == LOK_DOCTYPE_SPREADSHEET
)
418 GtkAdjustment
* pVAdjustment
= gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(window
->scrolledwindow
));
419 int rowSizePixel
= GTV_CALC_HEADER_BAR(window
->rowbar
)->m_nSizePixel
= gtk_adjustment_get_page_size(pVAdjustment
);
420 int rowPosPixel
= GTV_CALC_HEADER_BAR(window
->rowbar
)->m_nPositionPixel
= gtk_adjustment_get_value(pVAdjustment
);
421 GtkAdjustment
* pHAdjustment
= gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(window
->scrolledwindow
));
422 int colSizePixel
= GTV_CALC_HEADER_BAR(window
->columnbar
)->m_nSizePixel
= gtk_adjustment_get_page_size(pHAdjustment
);
423 int colPosPixel
= GTV_CALC_HEADER_BAR(window
->columnbar
)->m_nPositionPixel
= gtk_adjustment_get_value(pHAdjustment
);
425 std::stringstream aCommand
;
426 aCommand
<< ".uno:ViewRowColumnHeaders";
427 aCommand
<< "?x=" << int(lok_doc_view_pixel_to_twip(LOK_DOC_VIEW(window
->lokdocview
), colPosPixel
));
428 aCommand
<< "&width=" << int(lok_doc_view_pixel_to_twip(LOK_DOC_VIEW(window
->lokdocview
), colSizePixel
));
429 aCommand
<< "&y=" << int(lok_doc_view_pixel_to_twip(LOK_DOC_VIEW(window
->lokdocview
), rowPosPixel
));
430 aCommand
<< "&height=" << int(lok_doc_view_pixel_to_twip(LOK_DOC_VIEW(window
->lokdocview
), rowSizePixel
));
431 std::stringstream ss
;
432 ss
<< "lok::Document::getCommandValues(" << aCommand
.str() << ")";
433 g_info("%s", ss
.str().c_str());
434 char* pValues
= pDocument
->pClass
->getCommandValues(pDocument
, aCommand
.str().c_str());
435 g_info("lok::Document::getCommandValues() returned '%s'", pValues
);
436 std::stringstream
aStream(pValues
);
438 assert(!aStream
.str().empty());
439 boost::property_tree::ptree aTree
;
440 boost::property_tree::read_json(aStream
, aTree
);
442 gtv_calc_header_bar_configure(GTV_CALC_HEADER_BAR(window
->rowbar
), &aTree
.get_child("rows"));
443 gtv_calc_header_bar_configure(GTV_CALC_HEADER_BAR(window
->columnbar
), &aTree
.get_child("columns"));
444 gtv_calc_header_bar_configure(GTV_CALC_HEADER_BAR(window
->cornerarea
), nullptr);
450 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */