1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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/.
10 #include <sal/types.h>
18 #include <boost/property_tree/json_parser.hpp>
20 #include <com/sun/star/awt/Key.hpp>
21 #include <LibreOfficeKit/LibreOfficeKit.h>
22 #include <LibreOfficeKit/LibreOfficeKitInit.h>
23 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
24 #include <LibreOfficeKit/LibreOfficeKitGtk.h>
25 #include <vcl/event.hxx>
27 #include "tilebuffer.hxx"
29 #if !GLIB_CHECK_VERSION(2,32,0)
30 #define G_SOURCE_REMOVE FALSE
31 #define G_SOURCE_CONTINUE TRUE
33 #if !GLIB_CHECK_VERSION(2,40,0)
34 #define g_info(...) g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, __VA_ARGS__)
37 // Cursor bitmaps from the installation set.
38 #define CURSOR_HANDLE_DIR "/../share/libreofficekit/"
39 // Number of handles around a graphic selection.
40 #define GRAPHIC_HANDLE_COUNT 8
41 // Maximum Zoom allowed
43 // Minimum Zoom allowed
44 #define MIN_ZOOM 0.25f
46 /// This is expected to be locked during setView(), doSomethingElse() LOK calls.
47 static std::mutex g_aLOKMutex
;
51 /// Same as a GdkRectangle, but also tracks in which part the rectangle is.
55 GdkRectangle m_aRectangle
;
57 ViewRectangle(int nPart
= 0, const GdkRectangle
& rRectangle
= GdkRectangle())
59 m_aRectangle(rRectangle
)
64 /// Same as a list of GdkRectangles, but also tracks in which part the rectangle is.
68 std::vector
<GdkRectangle
> m_aRectangles
;
70 ViewRectangles(int nPart
= 0, std::vector
<GdkRectangle
>&& rRectangles
= std::vector
<GdkRectangle
>())
72 m_aRectangles(std::move(rRectangles
))
77 /// Private struct used by this GObject type
78 struct LOKDocViewPrivateImpl
80 std::string m_aLOPath
;
81 std::string m_aUserProfileURL
;
82 std::string m_aDocPath
;
83 std::string m_aRenderingArguments
;
84 gdouble m_nLoadProgress
;
86 bool m_bInit
; // initializeForRendering() has been called
90 LibreOfficeKit
* m_pOffice
;
91 LibreOfficeKitDocument
* m_pDocument
;
93 std::unique_ptr
<TileBuffer
> m_pTileBuffer
;
94 GThreadPool
* lokThreadPool
;
97 glong m_nDocumentWidthTwips
;
98 glong m_nDocumentHeightTwips
;
99 /// View or edit mode.
102 guint64 m_nLOKFeatures
;
103 /// Number of parts in currently loaded document
105 /// Position and size of the visible cursor.
106 GdkRectangle m_aVisibleCursor
;
107 /// Position and size of the view cursors. The current view can only see
108 /// them, can't modify them. Key is the view id.
109 std::map
<int, ViewRectangle
> m_aViewCursors
;
110 /// Cursor overlay is visible or hidden (for blinking).
111 bool m_bCursorOverlayVisible
;
112 /// Cursor is visible or hidden (e.g. for graphic selection).
113 bool m_bCursorVisible
;
114 /// Visibility of view selections. The current view can only see / them,
115 /// can't modify them. Key is the view id.
116 std::map
<int, bool> m_aViewCursorVisibilities
;
117 /// Time of the last button press.
118 guint32 m_nLastButtonPressTime
;
119 /// Time of the last button release.
120 guint32 m_nLastButtonReleaseTime
;
121 /// Last pressed button (left, right, middle)
122 guint32 m_nLastButtonPressed
;
123 /// Key modifier (ctrl, atl, shift)
124 guint32 m_nKeyModifier
;
125 /// Rectangles of the current text selection.
126 std::vector
<GdkRectangle
> m_aTextSelectionRectangles
;
127 /// Rectangles of the current content control.
128 std::vector
<GdkRectangle
> m_aContentControlRectangles
;
129 /// Alias/title of the current content control.
130 std::string m_aContentControlAlias
;
131 /// Rectangles of view selections. The current view can only see
132 /// them, can't modify them. Key is the view id.
133 std::map
<int, ViewRectangles
> m_aTextViewSelectionRectangles
;
134 /// Position and size of the selection start (as if there would be a cursor caret there).
135 GdkRectangle m_aTextSelectionStart
;
136 /// Position and size of the selection end.
137 GdkRectangle m_aTextSelectionEnd
;
138 GdkRectangle m_aGraphicSelection
;
139 /// Position and size of the graphic view selections. The current view can only
140 /// see them, can't modify them. Key is the view id.
141 std::map
<int, ViewRectangle
> m_aGraphicViewSelections
;
142 GdkRectangle m_aCellCursor
;
143 /// Position and size of the cell view cursors. The current view can only
144 /// see them, can't modify them. Key is the view id.
145 std::map
<int, ViewRectangle
> m_aCellViewCursors
;
146 bool m_bInDragGraphicSelection
;
147 /// Position, size and color of the reference marks. The current view can only
148 /// see them, can't modify them. Key is the view id.
149 std::vector
<std::pair
<ViewRectangle
, sal_uInt32
>> m_aReferenceMarks
;
151 /// @name Start/middle/end handle.
153 /// Bitmap of the text selection start handle.
154 cairo_surface_t
* m_pHandleStart
;
155 /// Rectangle of the text selection start handle, to know if the user clicked on it or not
156 GdkRectangle m_aHandleStartRect
;
157 /// If we are in the middle of a drag of the text selection end handle.
158 bool m_bInDragStartHandle
;
159 /// Bitmap of the text selection middle handle.
160 cairo_surface_t
* m_pHandleMiddle
;
161 /// Rectangle of the text selection middle handle, to know if the user clicked on it or not
162 GdkRectangle m_aHandleMiddleRect
;
163 /// If we are in the middle of a drag of the text selection middle handle.
164 bool m_bInDragMiddleHandle
;
165 /// Bitmap of the text selection end handle.
166 cairo_surface_t
* m_pHandleEnd
;
167 /// Rectangle of the text selection end handle, to know if the user clicked on it or not
168 GdkRectangle m_aHandleEndRect
;
169 /// If we are in the middle of a drag of the text selection end handle.
170 bool m_bInDragEndHandle
;
173 /// @name Graphic handles.
175 /// Rectangle of a graphic selection handle, to know if the user clicked on it or not.
176 GdkRectangle m_aGraphicHandleRects
[8];
177 /// If we are in the middle of a drag of a graphic selection handle.
178 bool m_bInDragGraphicHandles
[8];
181 /// View ID, returned by createView() or 0 by default.
184 /// Cached part ID, returned by getPart().
187 /// Cached document type, returned by getDocumentType().
188 LibreOfficeKitDocumentType m_eDocumentType
;
190 /// Contains a freshly set zoom level: logic size of a tile.
191 /// It gets reset back to 0 when LOK was informed about this zoom change.
192 int m_nTileSizeTwips
;
194 GdkRectangle m_aVisibleArea
;
195 bool m_bVisibleAreaSet
;
197 /// Event source ID for handleTimeout() of this widget.
200 /// Rectangles of view locks. The current view can only see
201 /// them, can't modify them. Key is the view id.
202 std::map
<int, ViewRectangle
> m_aViewLockRectangles
;
204 LOKDocViewPrivateImpl()
205 : m_nLoadProgress(0),
212 m_pDocument(nullptr),
213 lokThreadPool(nullptr),
215 m_nDocumentWidthTwips(0),
216 m_nDocumentHeightTwips(0),
220 m_aVisibleCursor({0, 0, 0, 0}),
221 m_bCursorOverlayVisible(false),
222 m_bCursorVisible(true),
223 m_nLastButtonPressTime(0),
224 m_nLastButtonReleaseTime(0),
225 m_nLastButtonPressed(0),
227 m_aTextSelectionStart({0, 0, 0, 0}),
228 m_aTextSelectionEnd({0, 0, 0, 0}),
229 m_aGraphicSelection({0, 0, 0, 0}),
230 m_aCellCursor({0, 0, 0, 0}),
231 m_bInDragGraphicSelection(false),
232 m_pHandleStart(nullptr),
233 m_aHandleStartRect({0, 0, 0, 0}),
234 m_bInDragStartHandle(false),
235 m_pHandleMiddle(nullptr),
236 m_aHandleMiddleRect({0, 0, 0, 0}),
237 m_bInDragMiddleHandle(false),
238 m_pHandleEnd(nullptr),
239 m_aHandleEndRect({0, 0, 0, 0}),
240 m_bInDragEndHandle(false),
243 m_eDocumentType(LOK_DOCTYPE_OTHER
),
245 m_aVisibleArea({0, 0, 0, 0}),
246 m_bVisibleAreaSet(false),
249 memset(&m_aGraphicHandleRects
, 0, sizeof(m_aGraphicHandleRects
));
250 memset(&m_bInDragGraphicHandles
, 0, sizeof(m_bInDragGraphicHandles
));
253 ~LOKDocViewPrivateImpl()
256 g_source_remove(m_nTimeoutId
);
260 // Must be run with g_aLOKMutex locked
261 void setDocumentView(LibreOfficeKitDocument
* pDoc
, int viewId
)
264 std::stringstream ss
;
265 ss
<< "lok::Document::setView(" << viewId
<< ")";
266 g_info("%s", ss
.str().c_str());
267 pDoc
->pClass
->setView(pDoc
, viewId
);
271 /// Wrapper around LOKDocViewPrivateImpl, managed by malloc/memset/free.
272 struct _LOKDocViewPrivate
274 LOKDocViewPrivateImpl
* m_pImpl
;
276 LOKDocViewPrivateImpl
* operator->()
314 PROP_USER_PROFILE_URL
,
327 PROP_DOC_PASSWORD_TO_MODIFY
,
328 PROP_TILED_ANNOTATIONS
,
333 static guint doc_view_signals
[LAST_SIGNAL
] = { 0 };
334 static GParamSpec
*properties
[PROP_LAST
] = { nullptr };
336 static void lok_doc_view_initable_iface_init (GInitableIface
*iface
);
337 static void callbackWorker (int nType
, const char* pPayload
, void* pData
);
338 static void updateClientZoom (LOKDocView
*pDocView
);
341 #pragma GCC diagnostic push
342 #pragma GCC diagnostic ignored "-Wunused-function"
343 #if defined __clang__
344 #if __has_warning("-Wdeprecated-volatile")
345 #pragma clang diagnostic ignored "-Wdeprecated-volatile"
349 G_DEFINE_TYPE_WITH_CODE (LOKDocView
, lok_doc_view
, GTK_TYPE_DRAWING_AREA
,
350 G_ADD_PRIVATE (LOKDocView
)
351 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE
, lok_doc_view_initable_iface_init
));
353 #pragma GCC diagnostic pop
356 static LOKDocViewPrivate
& getPrivate(LOKDocView
* pDocView
)
358 LOKDocViewPrivate
* priv
= static_cast<LOKDocViewPrivate
*>(lok_doc_view_get_instance_private(pDocView
));
364 /// Helper struct used to pass the data from soffice thread -> main thread.
368 std::string m_aPayload
;
369 LOKDocView
* m_pDocView
;
371 CallbackData(int nType
, const std::string
& rPayload
, LOKDocView
* pDocView
)
373 m_aPayload(rPayload
),
374 m_pDocView(pDocView
) {}
380 LOKPostCommand (LOKDocView
* pDocView
,
381 const gchar
* pCommand
,
382 const gchar
* pArguments
,
383 bool bNotifyWhenFinished
)
385 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
386 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
387 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_COMMAND
);
388 GError
* error
= nullptr;
389 pLOEvent
->m_pCommand
= g_strdup(pCommand
);
390 pLOEvent
->m_pArguments
= g_strdup(pArguments
);
391 pLOEvent
->m_bNotifyWhenFinished
= bNotifyWhenFinished
;
393 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
394 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
395 if (error
!= nullptr)
397 g_warning("Unable to call LOK_POST_COMMAND: %s", error
->message
);
398 g_clear_error(&error
);
400 g_object_unref(task
);
404 doSearch(LOKDocView
* pDocView
, const char* pText
, bool bBackwards
, bool highlightAll
)
406 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
407 if (!priv
->m_pDocument
)
410 boost::property_tree::ptree aTree
;
411 GtkWidget
* drawingWidget
= GTK_WIDGET(pDocView
);
412 GdkWindow
* drawingWindow
= gtk_widget_get_window(drawingWidget
);
415 std::shared_ptr
<cairo_region_t
> cairoVisRegion( gdk_window_get_visible_region(drawingWindow
),
416 cairo_region_destroy
);
417 cairo_rectangle_int_t cairoVisRect
;
418 cairo_region_get_rectangle(cairoVisRegion
.get(), 0, &cairoVisRect
);
419 int x
= pixelToTwip (cairoVisRect
.x
, priv
->m_fZoom
);
420 int y
= pixelToTwip (cairoVisRect
.y
, priv
->m_fZoom
);
422 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchString/type", '/'), "string");
423 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchString/value", '/'), pText
);
424 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.Backward/type", '/'), "boolean");
425 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.Backward/value", '/'), bBackwards
);
428 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.Command/type", '/'), "unsigned short");
429 // SvxSearchCmd::FIND_ALL
430 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.Command/value", '/'), "1");
433 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointX/type", '/'), "long");
434 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointX/value", '/'), x
);
435 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointY/type", '/'), "long");
436 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointY/value", '/'), y
);
438 std::stringstream aStream
;
439 boost::property_tree::write_json(aStream
, aTree
);
441 LOKPostCommand (pDocView
, ".uno:ExecuteSearch", aStream
.str().c_str(), false);
445 isEmptyRectangle(const GdkRectangle
& rRectangle
)
447 return rRectangle
.x
== 0 && rRectangle
.y
== 0 && rRectangle
.width
== 0 && rRectangle
.height
== 0;
450 /// if handled, returns TRUE else FALSE
452 handleTextSelectionOnButtonPress(GdkRectangle
& aClick
, LOKDocView
* pDocView
) {
453 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
455 if (gdk_rectangle_intersect(&aClick
, &priv
->m_aHandleStartRect
, nullptr))
457 g_info("LOKDocView_Impl::signalButton: start of drag start handle");
458 priv
->m_bInDragStartHandle
= true;
461 else if (gdk_rectangle_intersect(&aClick
, &priv
->m_aHandleMiddleRect
, nullptr))
463 g_info("LOKDocView_Impl::signalButton: start of drag middle handle");
464 priv
->m_bInDragMiddleHandle
= true;
467 else if (gdk_rectangle_intersect(&aClick
, &priv
->m_aHandleEndRect
, nullptr))
469 g_info("LOKDocView_Impl::signalButton: start of drag end handle");
470 priv
->m_bInDragEndHandle
= true;
477 /// if handled, returns TRUE else FALSE
479 handleGraphicSelectionOnButtonPress(GdkRectangle
& aClick
, LOKDocView
* pDocView
) {
480 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
481 GError
* error
= nullptr;
483 for (int i
= 0; i
< GRAPHIC_HANDLE_COUNT
; ++i
)
485 if (gdk_rectangle_intersect(&aClick
, &priv
->m_aGraphicHandleRects
[i
], nullptr))
487 g_info("LOKDocView_Impl::signalButton: start of drag graphic handle #%d", i
);
488 priv
->m_bInDragGraphicHandles
[i
] = true;
490 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
491 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_GRAPHIC_SELECTION
);
492 pLOEvent
->m_nSetGraphicSelectionType
= LOK_SETGRAPHICSELECTION_START
;
493 pLOEvent
->m_nSetGraphicSelectionX
= pixelToTwip(priv
->m_aGraphicHandleRects
[i
].x
+ priv
->m_aGraphicHandleRects
[i
].width
/ 2, priv
->m_fZoom
);
494 pLOEvent
->m_nSetGraphicSelectionY
= pixelToTwip(priv
->m_aGraphicHandleRects
[i
].y
+ priv
->m_aGraphicHandleRects
[i
].height
/ 2, priv
->m_fZoom
);
495 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
497 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
498 if (error
!= nullptr)
500 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error
->message
);
501 g_clear_error(&error
);
503 g_object_unref(task
);
512 /// if handled, returns TRUE else FALSE
514 handleTextSelectionOnButtonRelease(LOKDocView
* pDocView
) {
515 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
517 if (priv
->m_bInDragStartHandle
)
519 g_info("LOKDocView_Impl::signalButton: end of drag start handle");
520 priv
->m_bInDragStartHandle
= false;
523 else if (priv
->m_bInDragMiddleHandle
)
525 g_info("LOKDocView_Impl::signalButton: end of drag middle handle");
526 priv
->m_bInDragMiddleHandle
= false;
529 else if (priv
->m_bInDragEndHandle
)
531 g_info("LOKDocView_Impl::signalButton: end of drag end handle");
532 priv
->m_bInDragEndHandle
= false;
539 /// if handled, returns TRUE else FALSE
541 handleGraphicSelectionOnButtonRelease(LOKDocView
* pDocView
, GdkEventButton
* pEvent
) {
542 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
543 GError
* error
= nullptr;
545 for (int i
= 0; i
< GRAPHIC_HANDLE_COUNT
; ++i
)
547 if (priv
->m_bInDragGraphicHandles
[i
])
549 g_info("LOKDocView_Impl::signalButton: end of drag graphic handle #%d", i
);
550 priv
->m_bInDragGraphicHandles
[i
] = false;
552 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
553 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_GRAPHIC_SELECTION
);
554 pLOEvent
->m_nSetGraphicSelectionType
= LOK_SETGRAPHICSELECTION_END
;
555 pLOEvent
->m_nSetGraphicSelectionX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
556 pLOEvent
->m_nSetGraphicSelectionY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
557 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
559 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
560 if (error
!= nullptr)
562 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error
->message
);
563 g_clear_error(&error
);
565 g_object_unref(task
);
571 if (priv
->m_bInDragGraphicSelection
)
573 g_info("LOKDocView_Impl::signalButton: end of drag graphic selection");
574 priv
->m_bInDragGraphicSelection
= false;
576 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
577 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_GRAPHIC_SELECTION
);
578 pLOEvent
->m_nSetGraphicSelectionType
= LOK_SETGRAPHICSELECTION_END
;
579 pLOEvent
->m_nSetGraphicSelectionX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
580 pLOEvent
->m_nSetGraphicSelectionY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
581 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
583 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
584 if (error
!= nullptr)
586 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error
->message
);
587 g_clear_error(&error
);
589 g_object_unref(task
);
598 postKeyEventInThread(gpointer data
)
600 GTask
* task
= G_TASK(data
);
601 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
602 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
603 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
604 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
605 gint nTileSizePixelsScaled
= nTileSizePixels
* nScaleFactor
;
607 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
608 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
609 std::stringstream ss
;
611 if (priv
->m_nTileSizeTwips
)
613 ss
.str(std::string());
614 ss
<< "lok::Document::setClientZoom(" << nTileSizePixelsScaled
<< ", " << nTileSizePixelsScaled
<< ", " << priv
->m_nTileSizeTwips
<< ", " << priv
->m_nTileSizeTwips
<< ")";
615 g_info("%s", ss
.str().c_str());
616 priv
->m_pDocument
->pClass
->setClientZoom(priv
->m_pDocument
,
617 nTileSizePixelsScaled
,
618 nTileSizePixelsScaled
,
619 priv
->m_nTileSizeTwips
,
620 priv
->m_nTileSizeTwips
);
621 priv
->m_nTileSizeTwips
= 0;
623 if (priv
->m_bVisibleAreaSet
)
625 ss
.str(std::string());
626 ss
<< "lok::Document::setClientVisibleArea(" << priv
->m_aVisibleArea
.x
<< ", " << priv
->m_aVisibleArea
.y
<< ", ";
627 ss
<< priv
->m_aVisibleArea
.width
<< ", " << priv
->m_aVisibleArea
.height
<< ")";
628 g_info("%s", ss
.str().c_str());
629 priv
->m_pDocument
->pClass
->setClientVisibleArea(priv
->m_pDocument
,
630 priv
->m_aVisibleArea
.x
,
631 priv
->m_aVisibleArea
.y
,
632 priv
->m_aVisibleArea
.width
,
633 priv
->m_aVisibleArea
.height
);
634 priv
->m_bVisibleAreaSet
= false;
637 ss
.str(std::string());
638 ss
<< "lok::Document::postKeyEvent(" << pLOEvent
->m_nKeyEvent
<< ", " << pLOEvent
->m_nCharCode
<< ", " << pLOEvent
->m_nKeyCode
<< ")";
639 g_info("%s", ss
.str().c_str());
640 priv
->m_pDocument
->pClass
->postKeyEvent(priv
->m_pDocument
,
641 pLOEvent
->m_nKeyEvent
,
642 pLOEvent
->m_nCharCode
,
643 pLOEvent
->m_nKeyCode
);
647 signalKey (GtkWidget
* pWidget
, GdkEventKey
* pEvent
)
649 LOKDocView
* pDocView
= LOK_DOC_VIEW(pWidget
);
650 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
653 GError
* error
= nullptr;
657 g_info("signalKey: not in edit mode, ignore");
661 priv
->m_nKeyModifier
&= KEY_MOD2
;
662 switch (pEvent
->keyval
)
664 case GDK_KEY_BackSpace
:
665 nKeyCode
= com::sun::star::awt::Key::BACKSPACE
;
668 nKeyCode
= com::sun::star::awt::Key::DELETE
;
671 case GDK_KEY_KP_Enter
:
672 nKeyCode
= com::sun::star::awt::Key::RETURN
;
675 nKeyCode
= com::sun::star::awt::Key::ESCAPE
;
678 nKeyCode
= com::sun::star::awt::Key::TAB
;
681 nKeyCode
= com::sun::star::awt::Key::DOWN
;
684 nKeyCode
= com::sun::star::awt::Key::UP
;
687 nKeyCode
= com::sun::star::awt::Key::LEFT
;
690 nKeyCode
= com::sun::star::awt::Key::RIGHT
;
692 case GDK_KEY_Page_Down
:
693 nKeyCode
= com::sun::star::awt::Key::PAGEDOWN
;
695 case GDK_KEY_Page_Up
:
696 nKeyCode
= com::sun::star::awt::Key::PAGEUP
;
699 nKeyCode
= com::sun::star::awt::Key::INSERT
;
701 case GDK_KEY_Shift_L
:
702 case GDK_KEY_Shift_R
:
703 if (pEvent
->type
== GDK_KEY_PRESS
)
704 priv
->m_nKeyModifier
|= KEY_SHIFT
;
706 case GDK_KEY_Control_L
:
707 case GDK_KEY_Control_R
:
708 if (pEvent
->type
== GDK_KEY_PRESS
)
709 priv
->m_nKeyModifier
|= KEY_MOD1
;
713 if (pEvent
->type
== GDK_KEY_PRESS
)
714 priv
->m_nKeyModifier
|= KEY_MOD2
;
716 priv
->m_nKeyModifier
&= ~KEY_MOD2
;
719 if (pEvent
->keyval
>= GDK_KEY_F1
&& pEvent
->keyval
<= GDK_KEY_F26
)
720 nKeyCode
= com::sun::star::awt::Key::F1
+ (pEvent
->keyval
- GDK_KEY_F1
);
722 nCharCode
= gdk_keyval_to_unicode(pEvent
->keyval
);
725 // rsc is not public API, but should be good enough for debugging purposes.
726 // If this is needed for real, then probably a new param of type
727 // css::awt::KeyModifier is needed in postKeyEvent().
728 if (pEvent
->state
& GDK_SHIFT_MASK
)
729 nKeyCode
|= KEY_SHIFT
;
731 if (pEvent
->state
& GDK_CONTROL_MASK
)
732 nKeyCode
|= KEY_MOD1
;
734 if (priv
->m_nKeyModifier
& KEY_MOD2
)
735 nKeyCode
|= KEY_MOD2
;
737 if (nKeyCode
& (KEY_SHIFT
| KEY_MOD1
| KEY_MOD2
)) {
738 if (pEvent
->keyval
>= GDK_KEY_a
&& pEvent
->keyval
<= GDK_KEY_z
)
740 nKeyCode
|= 512 + (pEvent
->keyval
- GDK_KEY_a
);
742 else if (pEvent
->keyval
>= GDK_KEY_A
&& pEvent
->keyval
<= GDK_KEY_Z
) {
743 nKeyCode
|= 512 + (pEvent
->keyval
- GDK_KEY_A
);
745 else if (pEvent
->keyval
>= GDK_KEY_0
&& pEvent
->keyval
<= GDK_KEY_9
) {
746 nKeyCode
|= 256 + (pEvent
->keyval
- GDK_KEY_0
);
750 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
751 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_KEY
);
752 pLOEvent
->m_nKeyEvent
= pEvent
->type
== GDK_KEY_RELEASE
? LOK_KEYEVENT_KEYUP
: LOK_KEYEVENT_KEYINPUT
;
753 pLOEvent
->m_nCharCode
= nCharCode
;
754 pLOEvent
->m_nKeyCode
= nKeyCode
;
755 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
756 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
757 if (error
!= nullptr)
759 g_warning("Unable to call LOK_POST_KEY: %s", error
->message
);
760 g_clear_error(&error
);
762 g_object_unref(task
);
768 handleTimeout (gpointer pData
)
770 LOKDocView
* pDocView
= LOK_DOC_VIEW (pData
);
771 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
775 if (priv
->m_bCursorOverlayVisible
)
776 priv
->m_bCursorOverlayVisible
= false;
778 priv
->m_bCursorOverlayVisible
= true;
779 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
782 return G_SOURCE_CONTINUE
;
786 commandChanged(LOKDocView
* pDocView
, const std::string
& rString
)
788 g_signal_emit(pDocView
, doc_view_signals
[COMMAND_CHANGED
], 0, rString
.c_str());
792 searchNotFound(LOKDocView
* pDocView
, const std::string
& rString
)
794 g_signal_emit(pDocView
, doc_view_signals
[SEARCH_NOT_FOUND
], 0, rString
.c_str());
797 static void searchResultCount(LOKDocView
* pDocView
, const std::string
& rString
)
799 g_signal_emit(pDocView
, doc_view_signals
[SEARCH_RESULT_COUNT
], 0, rString
.c_str());
802 static void commandResult(LOKDocView
* pDocView
, const std::string
& rString
)
804 g_signal_emit(pDocView
, doc_view_signals
[COMMAND_RESULT
], 0, rString
.c_str());
807 static void addressChanged(LOKDocView
* pDocView
, const std::string
& rString
)
809 g_signal_emit(pDocView
, doc_view_signals
[ADDRESS_CHANGED
], 0, rString
.c_str());
812 static void formulaChanged(LOKDocView
* pDocView
, const std::string
& rString
)
814 g_signal_emit(pDocView
, doc_view_signals
[FORMULA_CHANGED
], 0, rString
.c_str());
817 static void reportError(LOKDocView
* /*pDocView*/, const std::string
& rString
)
819 GtkWidget
*dialog
= gtk_message_dialog_new(nullptr,
820 GTK_DIALOG_DESTROY_WITH_PARENT
,
825 gtk_dialog_run(GTK_DIALOG(dialog
));
826 gtk_widget_destroy(dialog
);
830 setPart(LOKDocView
* pDocView
, const std::string
& rString
)
832 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
833 priv
->m_nPartId
= std::stoi(rString
);
834 g_signal_emit(pDocView
, doc_view_signals
[PART_CHANGED
], 0, priv
->m_nPartId
);
838 hyperlinkClicked(LOKDocView
* pDocView
, const std::string
& rString
)
840 g_signal_emit(pDocView
, doc_view_signals
[HYPERLINK_CLICKED
], 0, rString
.c_str());
843 /// Trigger a redraw, invoked on the main thread by other functions running in a thread.
844 static gboolean
queueDraw(gpointer pData
)
846 GtkWidget
* pWidget
= static_cast<GtkWidget
*>(pData
);
848 gtk_widget_queue_draw(pWidget
);
850 return G_SOURCE_REMOVE
;
853 /// Looks up the author string from initializeForRendering()'s rendering arguments.
854 static std::string
getAuthorRenderingArgument(LOKDocViewPrivate
& priv
)
856 std::stringstream aStream
;
857 aStream
<< priv
->m_aRenderingArguments
;
858 boost::property_tree::ptree aTree
;
859 boost::property_tree::read_json(aStream
, aTree
);
861 for (const auto& rPair
: aTree
)
863 if (rPair
.first
== ".uno:Author")
865 aRet
= rPair
.second
.get
<std::string
>("value");
872 /// Author string <-> View ID map
873 static std::map
<std::string
, int> g_aAuthorViews
;
875 static void refreshSize(LOKDocView
* pDocView
)
877 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
879 priv
->m_pDocument
->pClass
->getDocumentSize(priv
->m_pDocument
, &priv
->m_nDocumentWidthTwips
, &priv
->m_nDocumentHeightTwips
);
880 float zoom
= priv
->m_fZoom
;
881 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
882 gint nTileSizePixelsScaled
= nTileSizePixels
* nScaleFactor
;
883 long nDocumentWidthTwips
= priv
->m_nDocumentWidthTwips
;
884 long nDocumentHeightTwips
= priv
->m_nDocumentHeightTwips
;
885 long nDocumentWidthPixels
= twipToPixel(nDocumentWidthTwips
, zoom
);
886 long nDocumentHeightPixels
= twipToPixel(nDocumentHeightTwips
, zoom
);
888 // Total number of columns in this document.
889 guint nColumns
= ceil(static_cast<double>(nDocumentWidthPixels
) / nTileSizePixelsScaled
);
890 priv
->m_pTileBuffer
= std::make_unique
<TileBuffer
>(nColumns
, nScaleFactor
);
891 gtk_widget_set_size_request(GTK_WIDGET(pDocView
),
892 nDocumentWidthPixels
,
893 nDocumentHeightPixels
);
896 /// Set up LOKDocView after the document is loaded, invoked on the main thread by openDocumentInThread() running in a thread.
897 static gboolean
postDocumentLoad(gpointer pData
)
899 LOKDocView
* pLOKDocView
= static_cast<LOKDocView
*>(pData
);
900 LOKDocViewPrivate
& priv
= getPrivate(pLOKDocView
);
902 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
903 priv
->m_pDocument
->pClass
->initializeForRendering(priv
->m_pDocument
, priv
->m_aRenderingArguments
.c_str());
904 // This returns the view id of the "current" view, but sadly if you load multiple documents that
905 // is apparently not a view showing the most recently loaded document. Not much we can do here,
906 // though. If that is fixed, this comment becomes incorrect.
907 priv
->m_nViewId
= priv
->m_pDocument
->pClass
->getView(priv
->m_pDocument
);
908 g_aAuthorViews
[getAuthorRenderingArgument(priv
)] = priv
->m_nViewId
;
909 priv
->m_pDocument
->pClass
->registerCallback(priv
->m_pDocument
, callbackWorker
, pLOKDocView
);
910 priv
->m_nParts
= priv
->m_pDocument
->pClass
->getParts(priv
->m_pDocument
);
912 priv
->m_nTimeoutId
= g_timeout_add(600, handleTimeout
, pLOKDocView
);
914 refreshSize(pLOKDocView
);
916 gtk_widget_set_can_focus(GTK_WIDGET(pLOKDocView
), true);
917 gtk_widget_grab_focus(GTK_WIDGET(pLOKDocView
));
918 lok_doc_view_set_zoom(pLOKDocView
, 1.0);
920 // we are completely loaded
921 priv
->m_bInit
= true;
922 g_object_notify_by_pspec(G_OBJECT(pLOKDocView
), properties
[PROP_IS_INITIALIZED
]);
924 return G_SOURCE_REMOVE
;
927 /// Implementation of the global callback handler, invoked by globalCallback();
929 globalCallback (gpointer pData
)
931 CallbackData
* pCallback
= static_cast<CallbackData
*>(pData
);
932 LOKDocViewPrivate
& priv
= getPrivate(pCallback
->m_pDocView
);
933 bool bModify
= false;
935 switch (pCallback
->m_nType
)
937 case LOK_CALLBACK_STATUS_INDICATOR_START
:
939 priv
->m_nLoadProgress
= 0.0;
940 g_signal_emit (pCallback
->m_pDocView
, doc_view_signals
[LOAD_CHANGED
], 0, 0.0);
943 case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE
:
945 priv
->m_nLoadProgress
= static_cast<gdouble
>(std::stoi(pCallback
->m_aPayload
)/100.0);
946 g_signal_emit (pCallback
->m_pDocView
, doc_view_signals
[LOAD_CHANGED
], 0, priv
->m_nLoadProgress
);
949 case LOK_CALLBACK_STATUS_INDICATOR_FINISH
:
951 priv
->m_nLoadProgress
= 1.0;
952 g_signal_emit (pCallback
->m_pDocView
, doc_view_signals
[LOAD_CHANGED
], 0, 1.0);
955 case LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY
:
958 case LOK_CALLBACK_DOCUMENT_PASSWORD
:
960 char const*const pURL(pCallback
->m_aPayload
.c_str());
961 g_signal_emit (pCallback
->m_pDocView
, doc_view_signals
[PASSWORD_REQUIRED
], 0, pURL
, bModify
);
964 case LOK_CALLBACK_ERROR
:
966 reportError(pCallback
->m_pDocView
, pCallback
->m_aPayload
);
969 case LOK_CALLBACK_SIGNATURE_STATUS
:
980 return G_SOURCE_REMOVE
;
984 globalCallbackWorker(int nType
, const char* pPayload
, void* pData
)
986 LOKDocView
* pDocView
= LOK_DOC_VIEW (pData
);
988 CallbackData
* pCallback
= new CallbackData(nType
, pPayload
? pPayload
: "(nil)", pDocView
);
989 g_info("LOKDocView_Impl::globalCallbackWorkerImpl: %s, '%s'", lokCallbackTypeToString(nType
), pPayload
);
990 gdk_threads_add_idle(globalCallback
, pCallback
);
994 payloadToRectangle (LOKDocView
* pDocView
, const char* pPayload
)
996 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
998 // x, y, width, height, part number.
999 gchar
** ppCoordinates
= g_strsplit(pPayload
, ", ", 5);
1000 gchar
** ppCoordinate
= ppCoordinates
;
1002 aRet
.width
= aRet
.height
= aRet
.x
= aRet
.y
= 0;
1006 g_strfreev(ppCoordinates
);
1009 aRet
.x
= atoi(*ppCoordinate
);
1015 g_strfreev(ppCoordinates
);
1018 aRet
.y
= atoi(*ppCoordinate
);
1024 g_strfreev(ppCoordinates
);
1027 long l
= atol(*ppCoordinate
);
1028 if (l
> std::numeric_limits
<int>::max())
1029 aRet
.width
= std::numeric_limits
<int>::max();
1032 if (aRet
.x
+ aRet
.width
> priv
->m_nDocumentWidthTwips
)
1033 aRet
.width
= priv
->m_nDocumentWidthTwips
- aRet
.x
;
1037 g_strfreev(ppCoordinates
);
1040 l
= atol(*ppCoordinate
);
1041 if (l
> std::numeric_limits
<int>::max())
1042 aRet
.height
= std::numeric_limits
<int>::max();
1045 if (aRet
.y
+ aRet
.height
> priv
->m_nDocumentHeightTwips
)
1046 aRet
.height
= priv
->m_nDocumentHeightTwips
- aRet
.y
;
1048 g_strfreev(ppCoordinates
);
1052 static std::vector
<GdkRectangle
>
1053 payloadToRectangles(LOKDocView
* pDocView
, const char* pPayload
)
1055 std::vector
<GdkRectangle
> aRet
;
1057 if (g_strcmp0(pPayload
, "EMPTY") == 0)
1060 gchar
** ppRectangles
= g_strsplit(pPayload
, "; ", 0);
1061 for (gchar
** ppRectangle
= ppRectangles
; *ppRectangle
; ++ppRectangle
)
1062 aRet
.push_back(payloadToRectangle(pDocView
, *ppRectangle
));
1063 g_strfreev(ppRectangles
);
1070 setTilesInvalid (LOKDocView
* pDocView
, const GdkRectangle
& rRectangle
)
1072 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1073 GdkRectangle aRectanglePixels
;
1074 GdkPoint aStart
, aEnd
;
1075 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
1076 gint nTileSizePixelsScaled
= nTileSizePixels
* nScaleFactor
;
1078 aRectanglePixels
.x
= twipToPixel(rRectangle
.x
, priv
->m_fZoom
) * nScaleFactor
;
1079 aRectanglePixels
.y
= twipToPixel(rRectangle
.y
, priv
->m_fZoom
) * nScaleFactor
;
1080 aRectanglePixels
.width
= twipToPixel(rRectangle
.width
, priv
->m_fZoom
) * nScaleFactor
;
1081 aRectanglePixels
.height
= twipToPixel(rRectangle
.height
, priv
->m_fZoom
) * nScaleFactor
;
1083 aStart
.x
= aRectanglePixels
.y
/ nTileSizePixelsScaled
;
1084 aStart
.y
= aRectanglePixels
.x
/ nTileSizePixelsScaled
;
1085 aEnd
.x
= (aRectanglePixels
.y
+ aRectanglePixels
.height
+ nTileSizePixelsScaled
) / nTileSizePixelsScaled
;
1086 aEnd
.y
= (aRectanglePixels
.x
+ aRectanglePixels
.width
+ nTileSizePixelsScaled
) / nTileSizePixelsScaled
;
1087 for (int i
= aStart
.x
; i
< aEnd
.x
; i
++)
1089 for (int j
= aStart
.y
; j
< aEnd
.y
; j
++)
1091 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
1092 priv
->m_pTileBuffer
->setInvalid(i
, j
, priv
->m_fZoom
, task
, priv
->lokThreadPool
);
1093 g_object_unref(task
);
1099 callback (gpointer pData
)
1101 CallbackData
* pCallback
= static_cast<CallbackData
*>(pData
);
1102 LOKDocView
* pDocView
= LOK_DOC_VIEW (pCallback
->m_pDocView
);
1103 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1105 //callback registered before the widget was destroyed.
1106 //Use existence of lokThreadPool as flag it was torn down
1107 if (!priv
->lokThreadPool
)
1110 return G_SOURCE_REMOVE
;
1113 switch (static_cast<LibreOfficeKitCallbackType
>(pCallback
->m_nType
))
1115 case LOK_CALLBACK_INVALIDATE_TILES
:
1117 if (pCallback
->m_aPayload
.compare(0, 5, "EMPTY") != 0) // payload doesn't start with "EMPTY"
1119 GdkRectangle aRectangle
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1120 setTilesInvalid(pDocView
, aRectangle
);
1123 priv
->m_pTileBuffer
->resetAllTiles();
1125 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1128 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
:
1131 std::stringstream
aStream(pCallback
->m_aPayload
);
1132 boost::property_tree::ptree aTree
;
1133 boost::property_tree::read_json(aStream
, aTree
);
1134 const std::string
& rRectangle
= aTree
.get
<std::string
>("rectangle");
1135 int nViewId
= aTree
.get
<int>("viewId");
1137 priv
->m_aVisibleCursor
= payloadToRectangle(pDocView
, rRectangle
.c_str());
1138 priv
->m_bCursorOverlayVisible
= true;
1139 if(nViewId
== priv
->m_nViewId
)
1141 g_signal_emit(pDocView
, doc_view_signals
[CURSOR_CHANGED
], 0,
1142 priv
->m_aVisibleCursor
.x
,
1143 priv
->m_aVisibleCursor
.y
,
1144 priv
->m_aVisibleCursor
.width
,
1145 priv
->m_aVisibleCursor
.height
);
1147 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1150 case LOK_CALLBACK_TEXT_SELECTION
:
1152 priv
->m_aTextSelectionRectangles
= payloadToRectangles(pDocView
, pCallback
->m_aPayload
.c_str());
1153 bool bIsTextSelected
= !priv
->m_aTextSelectionRectangles
.empty();
1154 // In case the selection is empty, then we get no LOK_CALLBACK_TEXT_SELECTION_START/END events.
1155 if (!bIsTextSelected
)
1157 memset(&priv
->m_aTextSelectionStart
, 0, sizeof(priv
->m_aTextSelectionStart
));
1158 memset(&priv
->m_aHandleStartRect
, 0, sizeof(priv
->m_aHandleStartRect
));
1159 memset(&priv
->m_aTextSelectionEnd
, 0, sizeof(priv
->m_aTextSelectionEnd
));
1160 memset(&priv
->m_aHandleEndRect
, 0, sizeof(priv
->m_aHandleEndRect
));
1163 memset(&priv
->m_aHandleMiddleRect
, 0, sizeof(priv
->m_aHandleMiddleRect
));
1165 g_signal_emit(pDocView
, doc_view_signals
[TEXT_SELECTION
], 0, bIsTextSelected
);
1166 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1169 case LOK_CALLBACK_TEXT_SELECTION_START
:
1171 priv
->m_aTextSelectionStart
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1174 case LOK_CALLBACK_TEXT_SELECTION_END
:
1176 priv
->m_aTextSelectionEnd
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1179 case LOK_CALLBACK_CURSOR_VISIBLE
:
1181 priv
->m_bCursorVisible
= pCallback
->m_aPayload
== "true";
1184 case LOK_CALLBACK_MOUSE_POINTER
:
1186 // We do not want the cursor to get changed in view-only mode
1189 // The gtk docs claim that most css cursors should be supported, however
1190 // on my system at least this is not true and many cursors are unsupported.
1191 // In this case pCursor = null, which results in the default cursor
1193 GdkCursor
* pCursor
= gdk_cursor_new_from_name(gtk_widget_get_display(GTK_WIDGET(pDocView
)),
1194 pCallback
->m_aPayload
.c_str());
1195 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(pDocView
)), pCursor
);
1199 case LOK_CALLBACK_GRAPHIC_SELECTION
:
1201 if (pCallback
->m_aPayload
!= "EMPTY")
1202 priv
->m_aGraphicSelection
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1204 memset(&priv
->m_aGraphicSelection
, 0, sizeof(priv
->m_aGraphicSelection
));
1205 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1208 case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION
:
1210 std::stringstream
aStream(pCallback
->m_aPayload
);
1211 boost::property_tree::ptree aTree
;
1212 boost::property_tree::read_json(aStream
, aTree
);
1213 int nViewId
= aTree
.get
<int>("viewId");
1214 int nPart
= aTree
.get
<int>("part");
1215 const std::string
& rRectangle
= aTree
.get
<std::string
>("selection");
1216 if (rRectangle
!= "EMPTY")
1217 priv
->m_aGraphicViewSelections
[nViewId
] = ViewRectangle(nPart
, payloadToRectangle(pDocView
, rRectangle
.c_str()));
1220 auto it
= priv
->m_aGraphicViewSelections
.find(nViewId
);
1221 if (it
!= priv
->m_aGraphicViewSelections
.end())
1222 priv
->m_aGraphicViewSelections
.erase(it
);
1224 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1228 case LOK_CALLBACK_CELL_CURSOR
:
1230 if (pCallback
->m_aPayload
!= "EMPTY")
1231 priv
->m_aCellCursor
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1233 memset(&priv
->m_aCellCursor
, 0, sizeof(priv
->m_aCellCursor
));
1234 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1237 case LOK_CALLBACK_HYPERLINK_CLICKED
:
1239 hyperlinkClicked(pDocView
, pCallback
->m_aPayload
);
1242 case LOK_CALLBACK_STATE_CHANGED
:
1244 commandChanged(pDocView
, pCallback
->m_aPayload
);
1247 case LOK_CALLBACK_SEARCH_NOT_FOUND
:
1249 searchNotFound(pDocView
, pCallback
->m_aPayload
);
1252 case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED
:
1254 refreshSize(pDocView
);
1255 g_signal_emit(pDocView
, doc_view_signals
[SIZE_CHANGED
], 0, nullptr);
1258 case LOK_CALLBACK_SET_PART
:
1260 setPart(pDocView
, pCallback
->m_aPayload
);
1263 case LOK_CALLBACK_SEARCH_RESULT_SELECTION
:
1265 boost::property_tree::ptree aTree
;
1266 std::stringstream
aStream(pCallback
->m_aPayload
);
1267 boost::property_tree::read_json(aStream
, aTree
);
1268 int nCount
= aTree
.get_child("searchResultSelection").size();
1269 searchResultCount(pDocView
, std::to_string(nCount
));
1272 case LOK_CALLBACK_UNO_COMMAND_RESULT
:
1274 commandResult(pDocView
, pCallback
->m_aPayload
);
1277 case LOK_CALLBACK_CELL_ADDRESS
:
1279 addressChanged(pDocView
, pCallback
->m_aPayload
);
1282 case LOK_CALLBACK_CELL_FORMULA
:
1284 formulaChanged(pDocView
, pCallback
->m_aPayload
);
1287 case LOK_CALLBACK_ERROR
:
1289 reportError(pDocView
, pCallback
->m_aPayload
);
1292 case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR
:
1294 std::stringstream
aStream(pCallback
->m_aPayload
);
1295 boost::property_tree::ptree aTree
;
1296 boost::property_tree::read_json(aStream
, aTree
);
1297 int nViewId
= aTree
.get
<int>("viewId");
1298 int nPart
= aTree
.get
<int>("part");
1299 const std::string
& rRectangle
= aTree
.get
<std::string
>("rectangle");
1300 priv
->m_aViewCursors
[nViewId
] = ViewRectangle(nPart
, payloadToRectangle(pDocView
, rRectangle
.c_str()));
1301 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1304 case LOK_CALLBACK_TEXT_VIEW_SELECTION
:
1306 std::stringstream
aStream(pCallback
->m_aPayload
);
1307 boost::property_tree::ptree aTree
;
1308 boost::property_tree::read_json(aStream
, aTree
);
1309 int nViewId
= aTree
.get
<int>("viewId");
1310 int nPart
= aTree
.get
<int>("part");
1311 const std::string
& rSelection
= aTree
.get
<std::string
>("selection");
1312 priv
->m_aTextViewSelectionRectangles
[nViewId
] = ViewRectangles(nPart
, payloadToRectangles(pDocView
, rSelection
.c_str()));
1313 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1316 case LOK_CALLBACK_VIEW_CURSOR_VISIBLE
:
1318 std::stringstream
aStream(pCallback
->m_aPayload
);
1319 boost::property_tree::ptree aTree
;
1320 boost::property_tree::read_json(aStream
, aTree
);
1321 int nViewId
= aTree
.get
<int>("viewId");
1322 const std::string
& rVisible
= aTree
.get
<std::string
>("visible");
1323 priv
->m_aViewCursorVisibilities
[nViewId
] = rVisible
== "true";
1324 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1328 case LOK_CALLBACK_CELL_VIEW_CURSOR
:
1330 std::stringstream
aStream(pCallback
->m_aPayload
);
1331 boost::property_tree::ptree aTree
;
1332 boost::property_tree::read_json(aStream
, aTree
);
1333 int nViewId
= aTree
.get
<int>("viewId");
1334 int nPart
= aTree
.get
<int>("part");
1335 const std::string
& rRectangle
= aTree
.get
<std::string
>("rectangle");
1336 if (rRectangle
!= "EMPTY")
1337 priv
->m_aCellViewCursors
[nViewId
] = ViewRectangle(nPart
, payloadToRectangle(pDocView
, rRectangle
.c_str()));
1340 auto it
= priv
->m_aCellViewCursors
.find(nViewId
);
1341 if (it
!= priv
->m_aCellViewCursors
.end())
1342 priv
->m_aCellViewCursors
.erase(it
);
1344 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1347 case LOK_CALLBACK_VIEW_LOCK
:
1349 std::stringstream
aStream(pCallback
->m_aPayload
);
1350 boost::property_tree::ptree aTree
;
1351 boost::property_tree::read_json(aStream
, aTree
);
1352 int nViewId
= aTree
.get
<int>("viewId");
1353 int nPart
= aTree
.get
<int>("part");
1354 const std::string
& rRectangle
= aTree
.get
<std::string
>("rectangle");
1355 if (rRectangle
!= "EMPTY")
1356 priv
->m_aViewLockRectangles
[nViewId
] = ViewRectangle(nPart
, payloadToRectangle(pDocView
, rRectangle
.c_str()));
1359 auto it
= priv
->m_aViewLockRectangles
.find(nViewId
);
1360 if (it
!= priv
->m_aViewLockRectangles
.end())
1361 priv
->m_aViewLockRectangles
.erase(it
);
1363 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1366 case LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED
:
1370 case LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED
:
1374 case LOK_CALLBACK_COMMENT
:
1375 g_signal_emit(pCallback
->m_pDocView
, doc_view_signals
[COMMENT
], 0, pCallback
->m_aPayload
.c_str());
1377 case LOK_CALLBACK_RULER_UPDATE
:
1378 g_signal_emit(pCallback
->m_pDocView
, doc_view_signals
[RULER
], 0, pCallback
->m_aPayload
.c_str());
1380 case LOK_CALLBACK_WINDOW
:
1381 g_signal_emit(pCallback
->m_pDocView
, doc_view_signals
[WINDOW
], 0, pCallback
->m_aPayload
.c_str());
1383 case LOK_CALLBACK_INVALIDATE_HEADER
:
1384 g_signal_emit(pCallback
->m_pDocView
, doc_view_signals
[INVALIDATE_HEADER
], 0, pCallback
->m_aPayload
.c_str());
1386 case LOK_CALLBACK_REFERENCE_MARKS
:
1388 std::stringstream
aStream(pCallback
->m_aPayload
);
1389 boost::property_tree::ptree aTree
;
1390 boost::property_tree::read_json(aStream
, aTree
);
1392 priv
->m_aReferenceMarks
.clear();
1394 for(const auto& rMark
: aTree
.get_child("marks"))
1396 sal_uInt32 nColor
= std::stoi(rMark
.second
.get
<std::string
>("color"), nullptr, 16);
1397 std::string sRect
= rMark
.second
.get
<std::string
>("rectangle");
1398 sal_uInt32 nPart
= std::stoi(rMark
.second
.get
<std::string
>("part"));
1400 GdkRectangle aRect
= payloadToRectangle(pDocView
, sRect
.c_str());
1401 priv
->m_aReferenceMarks
.push_back(std::pair
<ViewRectangle
, sal_uInt32
>(ViewRectangle(nPart
, aRect
), nColor
));
1404 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1408 case LOK_CALLBACK_CONTENT_CONTROL
:
1410 std::stringstream
aPayloadStream(pCallback
->m_aPayload
);
1411 boost::property_tree::ptree aTree
;
1412 boost::property_tree::read_json(aPayloadStream
, aTree
);
1413 auto aAction
= aTree
.get
<std::string
>("action");
1414 if (aAction
== "show")
1416 auto aRectangles
= aTree
.get
<std::string
>("rectangles");
1417 priv
->m_aContentControlRectangles
= payloadToRectangles(pDocView
, aRectangles
.c_str());
1419 auto it
= aTree
.find("alias");
1420 if (it
== aTree
.not_found())
1422 priv
->m_aContentControlAlias
.clear();
1426 priv
->m_aContentControlAlias
= it
->second
.get_value
<std::string
>();
1429 else if (aAction
== "hide")
1431 priv
->m_aContentControlRectangles
.clear();
1432 priv
->m_aContentControlAlias
.clear();
1434 else if (aAction
== "change-picture")
1436 GtkWidget
* pDialog
= gtk_file_chooser_dialog_new(
1437 "Open File", GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView
))),
1438 GTK_FILE_CHOOSER_ACTION_OPEN
, "Cancel", GTK_RESPONSE_CANCEL
, "Open",
1439 GTK_RESPONSE_ACCEPT
, nullptr);
1440 gint nRet
= gtk_dialog_run(GTK_DIALOG(pDialog
));
1441 if (nRet
== GTK_RESPONSE_ACCEPT
)
1443 GtkFileChooser
* pChooser
= GTK_FILE_CHOOSER(pDialog
);
1444 char* pFilename
= gtk_file_chooser_get_uri(pChooser
);
1445 boost::property_tree::ptree aValues
;
1446 aValues
.put("type", "picture");
1447 aValues
.put("changed", pFilename
);
1448 std::stringstream aStream
;
1449 boost::property_tree::write_json(aStream
, aValues
);
1450 std::string aJson
= aStream
.str();
1451 lok_doc_view_send_content_control_event(pDocView
, aJson
.c_str());
1455 gtk_widget_destroy(pDialog
);
1457 g_signal_emit(pCallback
->m_pDocView
, doc_view_signals
[CONTENT_CONTROL
], 0,
1458 pCallback
->m_aPayload
.c_str());
1459 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1463 case LOK_CALLBACK_STATUS_INDICATOR_START
:
1464 case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE
:
1465 case LOK_CALLBACK_STATUS_INDICATOR_FINISH
:
1466 case LOK_CALLBACK_DOCUMENT_PASSWORD
:
1467 case LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY
:
1468 case LOK_CALLBACK_VALIDITY_LIST_BUTTON
:
1469 case LOK_CALLBACK_VALIDITY_INPUT_HELP
:
1470 case LOK_CALLBACK_SIGNATURE_STATUS
:
1471 case LOK_CALLBACK_CONTEXT_MENU
:
1472 case LOK_CALLBACK_PROFILE_FRAME
:
1473 case LOK_CALLBACK_CLIPBOARD_CHANGED
:
1474 case LOK_CALLBACK_CONTEXT_CHANGED
:
1475 case LOK_CALLBACK_CELL_SELECTION_AREA
:
1476 case LOK_CALLBACK_CELL_AUTO_FILL_AREA
:
1477 case LOK_CALLBACK_TABLE_SELECTED
:
1478 case LOK_CALLBACK_JSDIALOG
:
1479 case LOK_CALLBACK_CALC_FUNCTION_LIST
:
1480 case LOK_CALLBACK_TAB_STOP_LIST
:
1481 case LOK_CALLBACK_FORM_FIELD_BUTTON
:
1482 case LOK_CALLBACK_INVALIDATE_SHEET_GEOMETRY
:
1483 case LOK_CALLBACK_DOCUMENT_BACKGROUND_COLOR
:
1484 case LOK_COMMAND_BLOCKED
:
1485 case LOK_CALLBACK_SC_FOLLOW_JUMP
:
1486 case LOK_CALLBACK_PRINT_RANGES
:
1487 case LOK_CALLBACK_FONTS_MISSING
:
1488 case LOK_CALLBACK_MEDIA_SHAPE
:
1489 case LOK_CALLBACK_EXPORT_FILE
:
1491 // TODO: Implement me
1497 return G_SOURCE_REMOVE
;
1500 static void callbackWorker (int nType
, const char* pPayload
, void* pData
)
1502 LOKDocView
* pDocView
= LOK_DOC_VIEW (pData
);
1504 CallbackData
* pCallback
= new CallbackData(nType
, pPayload
? pPayload
: "(nil)", pDocView
);
1505 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1506 std::stringstream ss
;
1507 ss
<< "callbackWorker, view #" << priv
->m_nViewId
<< ": " << lokCallbackTypeToString(nType
) << ", '" << (pPayload
? pPayload
: "(nil)") << "'";
1508 g_info("%s", ss
.str().c_str());
1509 gdk_threads_add_idle(callback
, pCallback
);
1513 renderHandle(LOKDocView
* pDocView
,
1515 const GdkRectangle
& rCursor
,
1516 cairo_surface_t
* pHandle
,
1517 GdkRectangle
& rRectangle
)
1519 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1520 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
1521 GdkPoint aCursorBottom
;
1522 int nHandleWidth
, nHandleHeight
;
1523 double fHandleScale
;
1525 nHandleWidth
= cairo_image_surface_get_width(pHandle
);
1526 nHandleHeight
= cairo_image_surface_get_height(pHandle
);
1527 // We want to scale down the handle, so that its height is the same as the cursor caret.
1528 fHandleScale
= twipToPixel(rCursor
.height
, priv
->m_fZoom
) / nHandleHeight
;
1529 // We want the top center of the handle bitmap to be at the bottom center of the cursor rectangle.
1530 aCursorBottom
.x
= twipToPixel(rCursor
.x
, priv
->m_fZoom
) + twipToPixel(rCursor
.width
, priv
->m_fZoom
) / 2 - (nHandleWidth
* fHandleScale
) / 2;
1531 aCursorBottom
.y
= twipToPixel(rCursor
.y
, priv
->m_fZoom
) + twipToPixel(rCursor
.height
, priv
->m_fZoom
);
1533 cairo_save (pCairo
);
1534 cairo_scale(pCairo
, 1.0 / nScaleFactor
, 1.0 / nScaleFactor
);
1535 cairo_translate(pCairo
, aCursorBottom
.x
* nScaleFactor
, aCursorBottom
.y
* nScaleFactor
);
1536 cairo_scale(pCairo
, fHandleScale
* nScaleFactor
, fHandleScale
* nScaleFactor
);
1537 cairo_set_source_surface(pCairo
, pHandle
, 0, 0);
1538 cairo_paint(pCairo
);
1539 cairo_restore (pCairo
);
1541 rRectangle
.x
= aCursorBottom
.x
;
1542 rRectangle
.y
= aCursorBottom
.y
;
1543 rRectangle
.width
= nHandleWidth
* fHandleScale
;
1544 rRectangle
.height
= nHandleHeight
* fHandleScale
;
1547 /// Renders handles around an rSelection rectangle on pCairo.
1549 renderGraphicHandle(LOKDocView
* pDocView
,
1551 const GdkRectangle
& rSelection
,
1552 const GdkRGBA
& rColor
)
1554 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1555 int nHandleWidth
= 9, nHandleHeight
= 9;
1556 GdkRectangle aSelection
;
1558 aSelection
.x
= twipToPixel(rSelection
.x
, priv
->m_fZoom
);
1559 aSelection
.y
= twipToPixel(rSelection
.y
, priv
->m_fZoom
);
1560 aSelection
.width
= twipToPixel(rSelection
.width
, priv
->m_fZoom
);
1561 aSelection
.height
= twipToPixel(rSelection
.height
, priv
->m_fZoom
);
1563 for (int i
= 0; i
< GRAPHIC_HANDLE_COUNT
; ++i
)
1565 int x
= aSelection
.x
, y
= aSelection
.y
;
1571 case 1: // top-middle
1572 x
+= aSelection
.width
/ 2;
1574 case 2: // top-right
1575 x
+= aSelection
.width
;
1577 case 3: // middle-left
1578 y
+= aSelection
.height
/ 2;
1580 case 4: // middle-right
1581 x
+= aSelection
.width
;
1582 y
+= aSelection
.height
/ 2;
1584 case 5: // bottom-left
1585 y
+= aSelection
.height
;
1587 case 6: // bottom-middle
1588 x
+= aSelection
.width
/ 2;
1589 y
+= aSelection
.height
;
1591 case 7: // bottom-right
1592 x
+= aSelection
.width
;
1593 y
+= aSelection
.height
;
1597 // Center the handle.
1598 x
-= nHandleWidth
/ 2;
1599 y
-= nHandleHeight
/ 2;
1601 priv
->m_aGraphicHandleRects
[i
].x
= x
;
1602 priv
->m_aGraphicHandleRects
[i
].y
= y
;
1603 priv
->m_aGraphicHandleRects
[i
].width
= nHandleWidth
;
1604 priv
->m_aGraphicHandleRects
[i
].height
= nHandleHeight
;
1606 cairo_set_source_rgb(pCairo
, rColor
.red
, rColor
.green
, rColor
.blue
);
1607 cairo_rectangle(pCairo
, x
, y
, nHandleWidth
, nHandleHeight
);
1612 /// Finishes the paint tile operation and returns the result, if any
1614 paintTileFinish(LOKDocView
* pDocView
, GAsyncResult
* res
, GError
**error
)
1616 GTask
* task
= G_TASK(res
);
1618 g_return_val_if_fail(LOK_IS_DOC_VIEW(pDocView
), nullptr);
1619 g_return_val_if_fail(g_task_is_valid(res
, pDocView
), nullptr);
1620 g_return_val_if_fail(error
== nullptr || *error
== nullptr, nullptr);
1622 return g_task_propagate_pointer(task
, error
);
1625 /// Callback called in the main UI thread when paintTileInThread in LOK thread has finished
1627 paintTileCallback(GObject
* sourceObject
, GAsyncResult
* res
, gpointer userData
)
1629 LOKDocView
* pDocView
= LOK_DOC_VIEW(sourceObject
);
1630 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1631 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(userData
);
1632 std::unique_ptr
<TileBuffer
>& buffer
= priv
->m_pTileBuffer
;
1636 cairo_surface_t
* pSurface
= static_cast<cairo_surface_t
*>(paintTileFinish(pDocView
, res
, &error
));
1637 if (error
!= nullptr)
1639 if (error
->domain
== LOK_TILEBUFFER_ERROR
&&
1640 error
->code
== LOK_TILEBUFFER_CHANGED
)
1641 g_info("Skipping paint tile request because corresponding"
1642 "tile buffer has been destroyed");
1644 g_warning("Unable to get painted GdkPixbuf: %s", error
->message
);
1645 g_error_free(error
);
1649 buffer
->setTile(pLOEvent
->m_nPaintTileX
, pLOEvent
->m_nPaintTileY
, pSurface
);
1650 gdk_threads_add_idle(queueDraw
, GTK_WIDGET(pDocView
));
1652 cairo_surface_destroy(pSurface
);
1657 renderDocument(LOKDocView
* pDocView
, cairo_t
* pCairo
)
1659 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1660 GdkRectangle aVisibleArea
;
1661 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
1662 gint nTileSizePixelsScaled
= nTileSizePixels
* nScaleFactor
;
1663 long nDocumentWidthPixels
= twipToPixel(priv
->m_nDocumentWidthTwips
, priv
->m_fZoom
) * nScaleFactor
;
1664 long nDocumentHeightPixels
= twipToPixel(priv
->m_nDocumentHeightTwips
, priv
->m_fZoom
) * nScaleFactor
;
1665 // Total number of rows / columns in this document.
1666 guint nRows
= ceil(static_cast<double>(nDocumentHeightPixels
) / nTileSizePixelsScaled
);
1667 guint nColumns
= ceil(static_cast<double>(nDocumentWidthPixels
) / nTileSizePixelsScaled
);
1669 cairo_save (pCairo
);
1670 cairo_scale (pCairo
, 1.0/nScaleFactor
, 1.0/nScaleFactor
);
1671 gdk_cairo_get_clip_rectangle (pCairo
, &aVisibleArea
);
1672 aVisibleArea
.x
= pixelToTwip (aVisibleArea
.x
, priv
->m_fZoom
);
1673 aVisibleArea
.y
= pixelToTwip (aVisibleArea
.y
, priv
->m_fZoom
);
1674 aVisibleArea
.width
= pixelToTwip (aVisibleArea
.width
, priv
->m_fZoom
);
1675 aVisibleArea
.height
= pixelToTwip (aVisibleArea
.height
, priv
->m_fZoom
);
1677 // Render the tiles.
1678 for (guint nRow
= 0; nRow
< nRows
; ++nRow
)
1680 for (guint nColumn
= 0; nColumn
< nColumns
; ++nColumn
)
1682 GdkRectangle aTileRectangleTwips
, aTileRectanglePixels
;
1685 // Determine size of the tile: the rightmost/bottommost tiles may
1686 // be smaller, and we need the size to decide if we need to repaint.
1687 if (nColumn
== nColumns
- 1)
1688 aTileRectanglePixels
.width
= nDocumentWidthPixels
- nColumn
* nTileSizePixelsScaled
;
1690 aTileRectanglePixels
.width
= nTileSizePixelsScaled
;
1691 if (nRow
== nRows
- 1)
1692 aTileRectanglePixels
.height
= nDocumentHeightPixels
- nRow
* nTileSizePixelsScaled
;
1694 aTileRectanglePixels
.height
= nTileSizePixelsScaled
;
1696 // Determine size and position of the tile in document coordinates,
1697 // so we can decide if we can skip painting for partial rendering.
1698 aTileRectangleTwips
.x
= pixelToTwip(nTileSizePixelsScaled
, priv
->m_fZoom
) * nColumn
;
1699 aTileRectangleTwips
.y
= pixelToTwip(nTileSizePixelsScaled
, priv
->m_fZoom
) * nRow
;
1700 aTileRectangleTwips
.width
= pixelToTwip(aTileRectanglePixels
.width
, priv
->m_fZoom
);
1701 aTileRectangleTwips
.height
= pixelToTwip(aTileRectanglePixels
.height
, priv
->m_fZoom
);
1703 if (!gdk_rectangle_intersect(&aVisibleArea
, &aTileRectangleTwips
, nullptr))
1708 LOEvent
* pLOEvent
= new LOEvent(LOK_PAINT_TILE
);
1709 pLOEvent
->m_nPaintTileX
= nRow
;
1710 pLOEvent
->m_nPaintTileY
= nColumn
;
1711 pLOEvent
->m_fPaintTileZoom
= priv
->m_fZoom
;
1712 pLOEvent
->m_pTileBuffer
= &*priv
->m_pTileBuffer
;
1713 GTask
* task
= g_task_new(pDocView
, nullptr, paintTileCallback
, pLOEvent
);
1714 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
1716 Tile
& currentTile
= priv
->m_pTileBuffer
->getTile(nRow
, nColumn
, task
, priv
->lokThreadPool
);
1717 cairo_surface_t
* pSurface
= currentTile
.getBuffer();
1718 cairo_set_source_surface(pCairo
, pSurface
,
1719 twipToPixel(aTileRectangleTwips
.x
, priv
->m_fZoom
),
1720 twipToPixel(aTileRectangleTwips
.y
, priv
->m_fZoom
));
1721 cairo_paint(pCairo
);
1722 g_object_unref(task
);
1727 cairo_restore (pCairo
);
1731 static const GdkRGBA
& getDarkColor(int nViewId
, LOKDocViewPrivate
& priv
)
1733 static std::map
<int, GdkRGBA
> aColorMap
;
1734 auto it
= aColorMap
.find(nViewId
);
1735 if (it
!= aColorMap
.end())
1738 if (priv
->m_eDocumentType
== LOK_DOCTYPE_TEXT
)
1740 char* pValues
= priv
->m_pDocument
->pClass
->getCommandValues(priv
->m_pDocument
, ".uno:TrackedChangeAuthors");
1741 std::stringstream aInfo
;
1742 aInfo
<< "lok::Document::getCommandValues('.uno:TrackedChangeAuthors') returned '" << pValues
<< "'" << std::endl
;
1743 g_info("%s", aInfo
.str().c_str());
1745 std::stringstream
aStream(pValues
);
1746 boost::property_tree::ptree aTree
;
1747 boost::property_tree::read_json(aStream
, aTree
);
1748 for (const auto& rValue
: aTree
.get_child("authors"))
1750 const std::string
& rName
= rValue
.second
.get
<std::string
>("name");
1751 guint32 nColor
= rValue
.second
.get
<guint32
>("color");
1752 GdkRGBA aColor
{static_cast<double>(static_cast<guint8
>(nColor
>>16))/255, static_cast<double>(static_cast<guint8
>(static_cast<guint16
>(nColor
) >> 8))/255, static_cast<double>(static_cast<guint8
>(nColor
))/255, 0};
1753 auto itAuthorViews
= g_aAuthorViews
.find(rName
);
1754 if (itAuthorViews
!= g_aAuthorViews
.end())
1755 aColorMap
[itAuthorViews
->second
] = aColor
;
1760 // Based on tools/color.hxx, COL_AUTHOR1_DARK..COL_AUTHOR9_DARK.
1761 static std::vector
<GdkRGBA
> aColors
=
1763 {(double(198))/255, (double(146))/255, (double(0))/255, 0},
1764 {(double(6))/255, (double(70))/255, (double(162))/255, 0},
1765 {(double(87))/255, (double(157))/255, (double(28))/255, 0},
1766 {(double(105))/255, (double(43))/255, (double(157))/255, 0},
1767 {(double(197))/255, (double(0))/255, (double(11))/255, 0},
1768 {(double(0))/255, (double(128))/255, (double(128))/255, 0},
1769 {(double(140))/255, (double(132))/255, (double(0))/255, 0},
1770 {(double(43))/255, (double(85))/255, (double(107))/255, 0},
1771 {(double(209))/255, (double(118))/255, (double(0))/255, 0},
1773 static int nColorCounter
= 0;
1774 GdkRGBA aColor
= aColors
[nColorCounter
++ % aColors
.size()];
1775 aColorMap
[nViewId
] = aColor
;
1777 assert(aColorMap
.find(nViewId
) != aColorMap
.end());
1778 return aColorMap
[nViewId
];
1782 renderOverlay(LOKDocView
* pDocView
, cairo_t
* pCairo
)
1784 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1786 if (priv
->m_bEdit
&& priv
->m_bCursorVisible
&& priv
->m_bCursorOverlayVisible
&& !isEmptyRectangle(priv
->m_aVisibleCursor
))
1788 if (priv
->m_aVisibleCursor
.width
< 30)
1789 // Set a minimal width if it would be 0.
1790 priv
->m_aVisibleCursor
.width
= 30;
1792 cairo_set_source_rgb(pCairo
, 0, 0, 0);
1793 cairo_rectangle(pCairo
,
1794 twipToPixel(priv
->m_aVisibleCursor
.x
, priv
->m_fZoom
),
1795 twipToPixel(priv
->m_aVisibleCursor
.y
, priv
->m_fZoom
),
1796 twipToPixel(priv
->m_aVisibleCursor
.width
, priv
->m_fZoom
),
1797 twipToPixel(priv
->m_aVisibleCursor
.height
, priv
->m_fZoom
));
1801 // View cursors: they do not blink and are colored.
1802 if (priv
->m_bEdit
&& !priv
->m_aViewCursors
.empty())
1804 for (auto& rPair
: priv
->m_aViewCursors
)
1806 auto itVisibility
= priv
->m_aViewCursorVisibilities
.find(rPair
.first
);
1807 if (itVisibility
!= priv
->m_aViewCursorVisibilities
.end() && !itVisibility
->second
)
1810 // Show view cursors when in Writer or when the part matches.
1811 if (rPair
.second
.m_nPart
!= priv
->m_nPartId
&& priv
->m_eDocumentType
!= LOK_DOCTYPE_TEXT
)
1814 GdkRectangle
& rCursor
= rPair
.second
.m_aRectangle
;
1815 if (rCursor
.width
< 30)
1816 // Set a minimal width if it would be 0.
1819 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
1820 cairo_set_source_rgb(pCairo
, rDark
.red
, rDark
.green
, rDark
.blue
);
1821 cairo_rectangle(pCairo
,
1822 twipToPixel(rCursor
.x
, priv
->m_fZoom
),
1823 twipToPixel(rCursor
.y
, priv
->m_fZoom
),
1824 twipToPixel(rCursor
.width
, priv
->m_fZoom
),
1825 twipToPixel(rCursor
.height
, priv
->m_fZoom
));
1830 if (priv
->m_bEdit
&& priv
->m_bCursorVisible
&& !isEmptyRectangle(priv
->m_aVisibleCursor
) && priv
->m_aTextSelectionRectangles
.empty())
1832 // Have a cursor, but no selection: we need the middle handle.
1833 gchar
* handleMiddlePath
= g_strconcat (priv
->m_aLOPath
.c_str(), CURSOR_HANDLE_DIR
, "handle_image_middle.png", nullptr);
1834 if (!priv
->m_pHandleMiddle
)
1836 priv
->m_pHandleMiddle
= cairo_image_surface_create_from_png(handleMiddlePath
);
1837 assert(cairo_surface_status(priv
->m_pHandleMiddle
) == CAIRO_STATUS_SUCCESS
);
1839 g_free (handleMiddlePath
);
1840 renderHandle(pDocView
, pCairo
, priv
->m_aVisibleCursor
, priv
->m_pHandleMiddle
, priv
->m_aHandleMiddleRect
);
1843 if (!priv
->m_aTextSelectionRectangles
.empty())
1845 for (const GdkRectangle
& rRectangle
: priv
->m_aTextSelectionRectangles
)
1847 // Blue with 75% transparency.
1848 cairo_set_source_rgba(pCairo
, (double(0x43))/255, (double(0xac))/255, (double(0xe8))/255, 0.25);
1849 cairo_rectangle(pCairo
,
1850 twipToPixel(rRectangle
.x
, priv
->m_fZoom
),
1851 twipToPixel(rRectangle
.y
, priv
->m_fZoom
),
1852 twipToPixel(rRectangle
.width
, priv
->m_fZoom
),
1853 twipToPixel(rRectangle
.height
, priv
->m_fZoom
));
1858 if (!isEmptyRectangle(priv
->m_aTextSelectionStart
))
1860 // Have a start position: we need a start handle.
1861 gchar
* handleStartPath
= g_strconcat (priv
->m_aLOPath
.c_str(), CURSOR_HANDLE_DIR
, "handle_image_start.png", nullptr);
1862 if (!priv
->m_pHandleStart
)
1864 priv
->m_pHandleStart
= cairo_image_surface_create_from_png(handleStartPath
);
1865 assert(cairo_surface_status(priv
->m_pHandleStart
) == CAIRO_STATUS_SUCCESS
);
1867 renderHandle(pDocView
, pCairo
, priv
->m_aTextSelectionStart
, priv
->m_pHandleStart
, priv
->m_aHandleStartRect
);
1868 g_free (handleStartPath
);
1870 if (!isEmptyRectangle(priv
->m_aTextSelectionEnd
))
1872 // Have a start position: we need an end handle.
1873 gchar
* handleEndPath
= g_strconcat (priv
->m_aLOPath
.c_str(), CURSOR_HANDLE_DIR
, "handle_image_end.png", nullptr);
1874 if (!priv
->m_pHandleEnd
)
1876 priv
->m_pHandleEnd
= cairo_image_surface_create_from_png(handleEndPath
);
1877 assert(cairo_surface_status(priv
->m_pHandleEnd
) == CAIRO_STATUS_SUCCESS
);
1879 renderHandle(pDocView
, pCairo
, priv
->m_aTextSelectionEnd
, priv
->m_pHandleEnd
, priv
->m_aHandleEndRect
);
1880 g_free (handleEndPath
);
1884 if (!priv
->m_aContentControlRectangles
.empty())
1886 for (const GdkRectangle
& rRectangle
: priv
->m_aContentControlRectangles
)
1888 // Black with 75% transparency.
1889 cairo_set_source_rgba(pCairo
, (double(0x7f))/255, (double(0x7f))/255, (double(0x7f))/255, 0.25);
1890 cairo_rectangle(pCairo
,
1891 twipToPixel(rRectangle
.x
, priv
->m_fZoom
),
1892 twipToPixel(rRectangle
.y
, priv
->m_fZoom
),
1893 twipToPixel(rRectangle
.width
, priv
->m_fZoom
),
1894 twipToPixel(rRectangle
.height
, priv
->m_fZoom
));
1898 if (!priv
->m_aContentControlAlias
.empty())
1900 cairo_text_extents_t aExtents
;
1901 cairo_text_extents(pCairo
, priv
->m_aContentControlAlias
.c_str(), &aExtents
);
1902 // Blue with 75% transparency.
1903 cairo_set_source_rgba(pCairo
, 0, 0, 1, 0.25);
1904 cairo_rectangle(pCairo
,
1905 twipToPixel(priv
->m_aContentControlRectangles
[0].x
, priv
->m_fZoom
) + aExtents
.x_bearing
,
1906 twipToPixel(priv
->m_aContentControlRectangles
[0].y
, priv
->m_fZoom
) + aExtents
.y_bearing
,
1911 cairo_move_to(pCairo
,
1912 twipToPixel(priv
->m_aContentControlRectangles
[0].x
, priv
->m_fZoom
),
1913 twipToPixel(priv
->m_aContentControlRectangles
[0].y
, priv
->m_fZoom
));
1914 cairo_set_source_rgb(pCairo
, 0, 0, 0);
1915 cairo_show_text(pCairo
, priv
->m_aContentControlAlias
.c_str());
1920 // Selections of other views.
1921 for (const auto& rPair
: priv
->m_aTextViewSelectionRectangles
)
1923 if (rPair
.second
.m_nPart
!= priv
->m_nPartId
&& priv
->m_eDocumentType
!= LOK_DOCTYPE_TEXT
)
1926 for (const GdkRectangle
& rRectangle
: rPair
.second
.m_aRectangles
)
1928 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
1929 // 75% transparency.
1930 cairo_set_source_rgba(pCairo
, rDark
.red
, rDark
.green
, rDark
.blue
, 0.25);
1931 cairo_rectangle(pCairo
,
1932 twipToPixel(rRectangle
.x
, priv
->m_fZoom
),
1933 twipToPixel(rRectangle
.y
, priv
->m_fZoom
),
1934 twipToPixel(rRectangle
.width
, priv
->m_fZoom
),
1935 twipToPixel(rRectangle
.height
, priv
->m_fZoom
));
1940 if (!isEmptyRectangle(priv
->m_aGraphicSelection
))
1942 GdkRGBA
const aBlack
{0, 0, 0, 0};
1943 renderGraphicHandle(pDocView
, pCairo
, priv
->m_aGraphicSelection
, aBlack
);
1946 // Graphic selections of other views.
1947 for (const auto& rPair
: priv
->m_aGraphicViewSelections
)
1949 const ViewRectangle
& rRectangle
= rPair
.second
;
1950 if (rRectangle
.m_nPart
!= priv
->m_nPartId
&& priv
->m_eDocumentType
!= LOK_DOCTYPE_TEXT
)
1953 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
1954 renderGraphicHandle(pDocView
, pCairo
, rRectangle
.m_aRectangle
, rDark
);
1957 // Draw the cell cursor.
1958 if (!isEmptyRectangle(priv
->m_aCellCursor
))
1960 cairo_set_source_rgb(pCairo
, 0, 0, 0);
1961 cairo_rectangle(pCairo
,
1962 twipToPixel(priv
->m_aCellCursor
.x
, priv
->m_fZoom
),
1963 twipToPixel(priv
->m_aCellCursor
.y
, priv
->m_fZoom
),
1964 twipToPixel(priv
->m_aCellCursor
.width
, priv
->m_fZoom
),
1965 twipToPixel(priv
->m_aCellCursor
.height
, priv
->m_fZoom
));
1966 cairo_set_line_width(pCairo
, 2.0);
1967 cairo_stroke(pCairo
);
1970 // Cell view cursors: they are colored.
1971 for (const auto& rPair
: priv
->m_aCellViewCursors
)
1973 const ViewRectangle
& rCursor
= rPair
.second
;
1974 if (rCursor
.m_nPart
!= priv
->m_nPartId
)
1977 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
1978 cairo_set_source_rgb(pCairo
, rDark
.red
, rDark
.green
, rDark
.blue
);
1979 cairo_rectangle(pCairo
,
1980 twipToPixel(rCursor
.m_aRectangle
.x
, priv
->m_fZoom
),
1981 twipToPixel(rCursor
.m_aRectangle
.y
, priv
->m_fZoom
),
1982 twipToPixel(rCursor
.m_aRectangle
.width
, priv
->m_fZoom
),
1983 twipToPixel(rCursor
.m_aRectangle
.height
, priv
->m_fZoom
));
1984 cairo_set_line_width(pCairo
, 2.0);
1985 cairo_stroke(pCairo
);
1988 // Draw reference marks.
1989 for (const auto& rPair
: priv
->m_aReferenceMarks
)
1991 const ViewRectangle
& rMark
= rPair
.first
;
1992 if (rMark
.m_nPart
!= priv
->m_nPartId
)
1995 sal_uInt32 nColor
= rPair
.second
;
1996 sal_uInt8 nRed
= (nColor
>> 16) & 0xff;
1997 sal_uInt8 nGreen
= (nColor
>> 8) & 0xff;
1998 sal_uInt8 nBlue
= nColor
& 0xff;
1999 cairo_set_source_rgb(pCairo
, nRed
, nGreen
, nBlue
);
2000 cairo_rectangle(pCairo
,
2001 twipToPixel(rMark
.m_aRectangle
.x
, priv
->m_fZoom
),
2002 twipToPixel(rMark
.m_aRectangle
.y
, priv
->m_fZoom
),
2003 twipToPixel(rMark
.m_aRectangle
.width
, priv
->m_fZoom
),
2004 twipToPixel(rMark
.m_aRectangle
.height
, priv
->m_fZoom
));
2005 cairo_set_line_width(pCairo
, 2.0);
2006 cairo_stroke(pCairo
);
2009 // View locks: they are colored.
2010 for (const auto& rPair
: priv
->m_aViewLockRectangles
)
2012 const ViewRectangle
& rRectangle
= rPair
.second
;
2013 if (rRectangle
.m_nPart
!= priv
->m_nPartId
)
2016 // Draw a rectangle.
2017 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
2018 cairo_set_source_rgb(pCairo
, rDark
.red
, rDark
.green
, rDark
.blue
);
2019 cairo_rectangle(pCairo
,
2020 twipToPixel(rRectangle
.m_aRectangle
.x
, priv
->m_fZoom
),
2021 twipToPixel(rRectangle
.m_aRectangle
.y
, priv
->m_fZoom
),
2022 twipToPixel(rRectangle
.m_aRectangle
.width
, priv
->m_fZoom
),
2023 twipToPixel(rRectangle
.m_aRectangle
.height
, priv
->m_fZoom
));
2024 cairo_set_line_width(pCairo
, 2.0);
2025 cairo_stroke(pCairo
);
2028 cairo_rectangle(pCairo
,
2029 twipToPixel(rRectangle
.m_aRectangle
.x
+ rRectangle
.m_aRectangle
.width
, priv
->m_fZoom
) - 25,
2030 twipToPixel(rRectangle
.m_aRectangle
.y
+ rRectangle
.m_aRectangle
.height
, priv
->m_fZoom
) - 15,
2035 twipToPixel(rRectangle
.m_aRectangle
.x
+ rRectangle
.m_aRectangle
.width
, priv
->m_fZoom
) - 15,
2036 twipToPixel(rRectangle
.m_aRectangle
.y
+ rRectangle
.m_aRectangle
.height
, priv
->m_fZoom
) - 15,
2040 cairo_stroke(pCairo
);
2047 lok_doc_view_signal_button(GtkWidget
* pWidget
, GdkEventButton
* pEvent
)
2049 LOKDocView
* pDocView
= LOK_DOC_VIEW (pWidget
);
2050 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2051 GError
* error
= nullptr;
2053 g_info("LOKDocView_Impl::signalButton: %d, %d (in twips: %d, %d)",
2054 static_cast<int>(pEvent
->x
), static_cast<int>(pEvent
->y
),
2055 static_cast<int>(pixelToTwip(pEvent
->x
, priv
->m_fZoom
)),
2056 static_cast<int>(pixelToTwip(pEvent
->y
, priv
->m_fZoom
)));
2057 gtk_widget_grab_focus(GTK_WIDGET(pDocView
));
2059 switch (pEvent
->type
)
2061 case GDK_BUTTON_PRESS
:
2063 GdkRectangle aClick
;
2064 aClick
.x
= pEvent
->x
;
2065 aClick
.y
= pEvent
->y
;
2069 if (handleTextSelectionOnButtonPress(aClick
, pDocView
))
2071 if (handleGraphicSelectionOnButtonPress(aClick
, pDocView
))
2075 if ((pEvent
->time
- priv
->m_nLastButtonPressTime
) < 250)
2077 priv
->m_nLastButtonPressTime
= pEvent
->time
;
2078 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
2079 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_MOUSE_EVENT
);
2080 pLOEvent
->m_nPostMouseEventType
= LOK_MOUSEEVENT_MOUSEBUTTONDOWN
;
2081 pLOEvent
->m_nPostMouseEventX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
2082 pLOEvent
->m_nPostMouseEventY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
2083 pLOEvent
->m_nPostMouseEventCount
= nCount
;
2084 switch (pEvent
->button
)
2087 pLOEvent
->m_nPostMouseEventButton
= MOUSE_LEFT
;
2090 pLOEvent
->m_nPostMouseEventButton
= MOUSE_MIDDLE
;
2093 pLOEvent
->m_nPostMouseEventButton
= MOUSE_RIGHT
;
2096 pLOEvent
->m_nPostMouseEventModifier
= priv
->m_nKeyModifier
;
2097 priv
->m_nLastButtonPressed
= pLOEvent
->m_nPostMouseEventButton
;
2098 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
2100 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
2101 if (error
!= nullptr)
2103 g_warning("Unable to call LOK_POST_MOUSE_EVENT: %s", error
->message
);
2104 g_clear_error(&error
);
2106 g_object_unref(task
);
2109 case GDK_BUTTON_RELEASE
:
2111 if (handleTextSelectionOnButtonRelease(pDocView
))
2113 if (handleGraphicSelectionOnButtonRelease(pDocView
, pEvent
))
2117 if ((pEvent
->time
- priv
->m_nLastButtonReleaseTime
) < 250)
2119 priv
->m_nLastButtonReleaseTime
= pEvent
->time
;
2120 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
2121 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_MOUSE_EVENT
);
2122 pLOEvent
->m_nPostMouseEventType
= LOK_MOUSEEVENT_MOUSEBUTTONUP
;
2123 pLOEvent
->m_nPostMouseEventX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
2124 pLOEvent
->m_nPostMouseEventY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
2125 pLOEvent
->m_nPostMouseEventCount
= nCount
;
2126 switch (pEvent
->button
)
2129 pLOEvent
->m_nPostMouseEventButton
= MOUSE_LEFT
;
2132 pLOEvent
->m_nPostMouseEventButton
= MOUSE_MIDDLE
;
2135 pLOEvent
->m_nPostMouseEventButton
= MOUSE_RIGHT
;
2138 pLOEvent
->m_nPostMouseEventModifier
= priv
->m_nKeyModifier
;
2139 priv
->m_nLastButtonPressed
= pLOEvent
->m_nPostMouseEventButton
;
2140 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
2142 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
2143 if (error
!= nullptr)
2145 g_warning("Unable to call LOK_POST_MOUSE_EVENT: %s", error
->message
);
2146 g_clear_error(&error
);
2148 g_object_unref(task
);
2158 getDragPoint(GdkRectangle
* pHandle
,
2159 GdkEventMotion
* pEvent
,
2162 GdkPoint aCursor
, aHandle
;
2164 // Center of the cursor rectangle: we know that it's above the handle.
2165 aCursor
.x
= pHandle
->x
+ pHandle
->width
/ 2;
2166 aCursor
.y
= pHandle
->y
- pHandle
->height
/ 2;
2167 // Center of the handle rectangle.
2168 aHandle
.x
= pHandle
->x
+ pHandle
->width
/ 2;
2169 aHandle
.y
= pHandle
->y
+ pHandle
->height
/ 2;
2170 // Our target is the original cursor position + the dragged offset.
2171 pPoint
->x
= aCursor
.x
+ (pEvent
->x
- aHandle
.x
);
2172 pPoint
->y
= aCursor
.y
+ (pEvent
->y
- aHandle
.y
);
2176 lok_doc_view_signal_motion (GtkWidget
* pWidget
, GdkEventMotion
* pEvent
)
2178 LOKDocView
* pDocView
= LOK_DOC_VIEW (pWidget
);
2179 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2181 GError
* error
= nullptr;
2183 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2184 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
2185 if (priv
->m_bInDragMiddleHandle
)
2187 g_info("lcl_signalMotion: dragging the middle handle");
2188 getDragPoint(&priv
->m_aHandleMiddleRect
, pEvent
, &aPoint
);
2189 priv
->m_pDocument
->pClass
->setTextSelection(priv
->m_pDocument
, LOK_SETTEXTSELECTION_RESET
, pixelToTwip(aPoint
.x
, priv
->m_fZoom
), pixelToTwip(aPoint
.y
, priv
->m_fZoom
));
2192 if (priv
->m_bInDragStartHandle
)
2194 g_info("lcl_signalMotion: dragging the start handle");
2195 getDragPoint(&priv
->m_aHandleStartRect
, pEvent
, &aPoint
);
2196 priv
->m_pDocument
->pClass
->setTextSelection(priv
->m_pDocument
, LOK_SETTEXTSELECTION_START
, pixelToTwip(aPoint
.x
, priv
->m_fZoom
), pixelToTwip(aPoint
.y
, priv
->m_fZoom
));
2199 if (priv
->m_bInDragEndHandle
)
2201 g_info("lcl_signalMotion: dragging the end handle");
2202 getDragPoint(&priv
->m_aHandleEndRect
, pEvent
, &aPoint
);
2203 priv
->m_pDocument
->pClass
->setTextSelection(priv
->m_pDocument
, LOK_SETTEXTSELECTION_END
, pixelToTwip(aPoint
.x
, priv
->m_fZoom
), pixelToTwip(aPoint
.y
, priv
->m_fZoom
));
2207 for (int i
= 0; i
< GRAPHIC_HANDLE_COUNT
; ++i
)
2209 if (priv
->m_bInDragGraphicHandles
[i
])
2211 g_info("lcl_signalMotion: dragging the graphic handle #%d", i
);
2215 if (priv
->m_bInDragGraphicSelection
)
2217 g_info("lcl_signalMotion: dragging the graphic selection");
2221 GdkRectangle aMotionInTwipsInTwips
;
2222 aMotionInTwipsInTwips
.x
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
2223 aMotionInTwipsInTwips
.y
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
2224 aMotionInTwipsInTwips
.width
= 1;
2225 aMotionInTwipsInTwips
.height
= 1;
2226 if (gdk_rectangle_intersect(&aMotionInTwipsInTwips
, &priv
->m_aGraphicSelection
, nullptr))
2228 g_info("lcl_signalMotion: start of drag graphic selection");
2229 priv
->m_bInDragGraphicSelection
= true;
2231 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
2232 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_GRAPHIC_SELECTION
);
2233 pLOEvent
->m_nSetGraphicSelectionType
= LOK_SETGRAPHICSELECTION_START
;
2234 pLOEvent
->m_nSetGraphicSelectionX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
2235 pLOEvent
->m_nSetGraphicSelectionY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
2236 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
2238 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
2239 if (error
!= nullptr)
2241 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error
->message
);
2242 g_clear_error(&error
);
2244 g_object_unref(task
);
2249 // Otherwise a mouse move, as on the desktop.
2251 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
2252 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_MOUSE_EVENT
);
2253 pLOEvent
->m_nPostMouseEventType
= LOK_MOUSEEVENT_MOUSEMOVE
;
2254 pLOEvent
->m_nPostMouseEventX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
2255 pLOEvent
->m_nPostMouseEventY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
2256 pLOEvent
->m_nPostMouseEventCount
= 1;
2257 pLOEvent
->m_nPostMouseEventButton
= priv
->m_nLastButtonPressed
;
2258 pLOEvent
->m_nPostMouseEventModifier
= priv
->m_nKeyModifier
;
2260 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
2262 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
2263 if (error
!= nullptr)
2265 g_warning("Unable to call LOK_MOUSEEVENT_MOUSEMOVE: %s", error
->message
);
2266 g_clear_error(&error
);
2268 g_object_unref(task
);
2274 setGraphicSelectionInThread(gpointer data
)
2276 GTask
* task
= G_TASK(data
);
2277 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2278 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2279 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2281 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2282 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
2283 std::stringstream ss
;
2284 ss
<< "lok::Document::setGraphicSelection(" << pLOEvent
->m_nSetGraphicSelectionType
;
2285 ss
<< ", " << pLOEvent
->m_nSetGraphicSelectionX
;
2286 ss
<< ", " << pLOEvent
->m_nSetGraphicSelectionY
<< ")";
2287 g_info("%s", ss
.str().c_str());
2288 priv
->m_pDocument
->pClass
->setGraphicSelection(priv
->m_pDocument
,
2289 pLOEvent
->m_nSetGraphicSelectionType
,
2290 pLOEvent
->m_nSetGraphicSelectionX
,
2291 pLOEvent
->m_nSetGraphicSelectionY
);
2295 setClientZoomInThread(gpointer data
)
2297 GTask
* task
= G_TASK(data
);
2298 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2299 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2300 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2302 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2303 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
2304 priv
->m_pDocument
->pClass
->setClientZoom(priv
->m_pDocument
,
2305 pLOEvent
->m_nTilePixelWidth
,
2306 pLOEvent
->m_nTilePixelHeight
,
2307 pLOEvent
->m_nTileTwipWidth
,
2308 pLOEvent
->m_nTileTwipHeight
);
2312 postMouseEventInThread(gpointer data
)
2314 GTask
* task
= G_TASK(data
);
2315 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2316 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2317 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2319 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2320 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
2321 std::stringstream ss
;
2322 ss
<< "lok::Document::postMouseEvent(" << pLOEvent
->m_nPostMouseEventType
;
2323 ss
<< ", " << pLOEvent
->m_nPostMouseEventX
;
2324 ss
<< ", " << pLOEvent
->m_nPostMouseEventY
;
2325 ss
<< ", " << pLOEvent
->m_nPostMouseEventCount
;
2326 ss
<< ", " << pLOEvent
->m_nPostMouseEventButton
;
2327 ss
<< ", " << pLOEvent
->m_nPostMouseEventModifier
<< ")";
2328 g_info("%s", ss
.str().c_str());
2329 priv
->m_pDocument
->pClass
->postMouseEvent(priv
->m_pDocument
,
2330 pLOEvent
->m_nPostMouseEventType
,
2331 pLOEvent
->m_nPostMouseEventX
,
2332 pLOEvent
->m_nPostMouseEventY
,
2333 pLOEvent
->m_nPostMouseEventCount
,
2334 pLOEvent
->m_nPostMouseEventButton
,
2335 pLOEvent
->m_nPostMouseEventModifier
);
2339 openDocumentInThread (gpointer data
)
2341 GTask
* task
= G_TASK(data
);
2342 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2343 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2345 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2346 if ( priv
->m_pDocument
)
2348 priv
->m_pDocument
->pClass
->destroy( priv
->m_pDocument
);
2349 priv
->m_pDocument
= nullptr;
2352 priv
->m_pOffice
->pClass
->registerCallback(priv
->m_pOffice
, globalCallbackWorker
, pDocView
);
2353 std::string url
= priv
->m_aDocPath
;
2354 if (gchar
* pURL
= g_filename_to_uri(url
.c_str(), nullptr, nullptr))
2359 priv
->m_pDocument
= priv
->m_pOffice
->pClass
->documentLoadWithOptions( priv
->m_pOffice
, url
.c_str(), "en-US" );
2360 if ( !priv
->m_pDocument
)
2362 char *pError
= priv
->m_pOffice
->pClass
->getError( priv
->m_pOffice
);
2363 g_task_return_new_error(task
, g_quark_from_static_string ("LOK error"), 0, "%s", pError
);
2367 priv
->m_eDocumentType
= static_cast<LibreOfficeKitDocumentType
>(priv
->m_pDocument
->pClass
->getDocumentType(priv
->m_pDocument
));
2368 gdk_threads_add_idle(postDocumentLoad
, pDocView
);
2369 g_task_return_boolean (task
, true);
2374 setPartInThread(gpointer data
)
2376 GTask
* task
= G_TASK(data
);
2377 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2378 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2379 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2380 int nPart
= pLOEvent
->m_nPart
;
2382 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2383 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
2384 priv
->m_pDocument
->pClass
->setPart( priv
->m_pDocument
, nPart
);
2387 lok_doc_view_reset_view(pDocView
);
2391 setPartmodeInThread(gpointer data
)
2393 GTask
* task
= G_TASK(data
);
2394 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2395 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2396 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2397 int nPartMode
= pLOEvent
->m_nPartMode
;
2399 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2400 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
2401 priv
->m_pDocument
->pClass
->setPartMode( priv
->m_pDocument
, nPartMode
);
2405 setEditInThread(gpointer data
)
2407 GTask
* task
= G_TASK(data
);
2408 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2409 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2410 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2411 bool bWasEdit
= priv
->m_bEdit
;
2412 bool bEdit
= pLOEvent
->m_bEdit
;
2414 if (!priv
->m_bEdit
&& bEdit
)
2415 g_info("lok_doc_view_set_edit: entering edit mode");
2416 else if (priv
->m_bEdit
&& !bEdit
)
2418 g_info("lok_doc_view_set_edit: leaving edit mode");
2419 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2420 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
2421 priv
->m_pDocument
->pClass
->resetSelection(priv
->m_pDocument
);
2423 priv
->m_bEdit
= bEdit
;
2424 g_signal_emit(pDocView
, doc_view_signals
[EDIT_CHANGED
], 0, bWasEdit
);
2425 gdk_threads_add_idle(queueDraw
, GTK_WIDGET(pDocView
));
2429 postCommandInThread (gpointer data
)
2431 GTask
* task
= G_TASK(data
);
2432 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2433 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2434 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2436 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2437 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
2438 std::stringstream ss
;
2439 ss
<< "lok::Document::postUnoCommand(" << pLOEvent
->m_pCommand
<< ", " << pLOEvent
->m_pArguments
<< ")";
2440 g_info("%s", ss
.str().c_str());
2441 priv
->m_pDocument
->pClass
->postUnoCommand(priv
->m_pDocument
, pLOEvent
->m_pCommand
, pLOEvent
->m_pArguments
, pLOEvent
->m_bNotifyWhenFinished
);
2445 paintTileInThread (gpointer data
)
2447 GTask
* task
= G_TASK(data
);
2448 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2449 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2450 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2451 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
2452 gint nTileSizePixelsScaled
= nTileSizePixels
* nScaleFactor
;
2454 // check if "source" tile buffer is different from "current" tile buffer
2455 if (pLOEvent
->m_pTileBuffer
!= &*priv
->m_pTileBuffer
)
2457 pLOEvent
->m_pTileBuffer
= nullptr;
2458 g_task_return_new_error(task
,
2459 LOK_TILEBUFFER_ERROR
,
2460 LOK_TILEBUFFER_CHANGED
,
2461 "TileBuffer has changed");
2464 std::unique_ptr
<TileBuffer
>& buffer
= priv
->m_pTileBuffer
;
2465 if (buffer
->hasValidTile(pLOEvent
->m_nPaintTileX
, pLOEvent
->m_nPaintTileY
))
2468 cairo_surface_t
*pSurface
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32
, nTileSizePixelsScaled
, nTileSizePixelsScaled
);
2469 if (cairo_surface_status(pSurface
) != CAIRO_STATUS_SUCCESS
)
2471 cairo_surface_destroy(pSurface
);
2472 g_task_return_new_error(task
,
2473 LOK_TILEBUFFER_ERROR
,
2474 LOK_TILEBUFFER_MEMORY
,
2475 "Error allocating Surface");
2479 unsigned char* pBuffer
= cairo_image_surface_get_data(pSurface
);
2480 GdkRectangle aTileRectangle
;
2481 aTileRectangle
.x
= pixelToTwip(nTileSizePixelsScaled
, pLOEvent
->m_fPaintTileZoom
* nScaleFactor
) * pLOEvent
->m_nPaintTileY
;
2482 aTileRectangle
.y
= pixelToTwip(nTileSizePixelsScaled
, pLOEvent
->m_fPaintTileZoom
* nScaleFactor
) * pLOEvent
->m_nPaintTileX
;
2484 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2485 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
2486 std::stringstream ss
;
2487 GTimer
* aTimer
= g_timer_new();
2489 ss
<< "lok::Document::paintTile(" << static_cast<void*>(pBuffer
) << ", "
2490 << nTileSizePixelsScaled
<< ", " << nTileSizePixelsScaled
<< ", "
2491 << aTileRectangle
.x
<< ", " << aTileRectangle
.y
<< ", "
2492 << pixelToTwip(nTileSizePixelsScaled
, pLOEvent
->m_fPaintTileZoom
* nScaleFactor
) << ", "
2493 << pixelToTwip(nTileSizePixelsScaled
, pLOEvent
->m_fPaintTileZoom
* nScaleFactor
) << ")";
2495 priv
->m_pDocument
->pClass
->paintTile(priv
->m_pDocument
,
2497 nTileSizePixelsScaled
, nTileSizePixelsScaled
,
2498 aTileRectangle
.x
, aTileRectangle
.y
,
2499 pixelToTwip(nTileSizePixelsScaled
, pLOEvent
->m_fPaintTileZoom
* nScaleFactor
),
2500 pixelToTwip(nTileSizePixelsScaled
, pLOEvent
->m_fPaintTileZoom
* nScaleFactor
));
2503 g_timer_elapsed(aTimer
, &nElapsedMs
);
2504 ss
<< " rendered in " << (nElapsedMs
/ 1000.) << " milliseconds";
2505 g_info("%s", ss
.str().c_str());
2506 g_timer_destroy(aTimer
);
2508 cairo_surface_mark_dirty(pSurface
);
2510 // Its likely that while the tilebuffer has changed, one of the paint tile
2511 // requests has passed the previous check at start of this function, and has
2512 // rendered the tile already. We want to stop such rendered tiles from being
2513 // stored in new tile buffer.
2514 if (pLOEvent
->m_pTileBuffer
!= &*priv
->m_pTileBuffer
)
2516 pLOEvent
->m_pTileBuffer
= nullptr;
2517 g_task_return_new_error(task
,
2518 LOK_TILEBUFFER_ERROR
,
2519 LOK_TILEBUFFER_CHANGED
,
2520 "TileBuffer has changed");
2524 g_task_return_pointer(task
, pSurface
, reinterpret_cast<GDestroyNotify
>(cairo_surface_destroy
));
2529 lokThreadFunc(gpointer data
, gpointer
/*user_data*/)
2531 GTask
* task
= G_TASK(data
);
2532 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2533 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2534 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2536 switch (pLOEvent
->m_nType
)
2539 openDocumentInThread(task
);
2541 case LOK_POST_COMMAND
:
2542 postCommandInThread(task
);
2545 setEditInThread(task
);
2548 setPartInThread(task
);
2550 case LOK_SET_PARTMODE
:
2551 setPartmodeInThread(task
);
2554 // view-only/editable mode already checked during signal key signal emission
2555 postKeyEventInThread(task
);
2557 case LOK_PAINT_TILE
:
2558 paintTileInThread(task
);
2560 case LOK_POST_MOUSE_EVENT
:
2561 postMouseEventInThread(task
);
2563 case LOK_SET_GRAPHIC_SELECTION
:
2565 setGraphicSelectionInThread(task
);
2567 g_info ("LOK_SET_GRAPHIC_SELECTION: skipping graphical operation in view-only mode");
2569 case LOK_SET_CLIENT_ZOOM
:
2570 setClientZoomInThread(task
);
2574 g_object_unref(task
);
2578 onStyleContextChanged (LOKDocView
* pDocView
)
2580 // The scale factor might have changed
2581 updateClientZoom (pDocView
);
2582 gtk_widget_queue_draw (GTK_WIDGET (pDocView
));
2585 static void lok_doc_view_init (LOKDocView
* pDocView
)
2587 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2588 priv
.m_pImpl
= new LOKDocViewPrivateImpl();
2590 gtk_widget_add_events(GTK_WIDGET(pDocView
),
2591 GDK_BUTTON_PRESS_MASK
2592 |GDK_BUTTON_RELEASE_MASK
2593 |GDK_BUTTON_MOTION_MASK
2595 |GDK_KEY_RELEASE_MASK
);
2597 priv
->lokThreadPool
= g_thread_pool_new(lokThreadFunc
,
2603 g_signal_connect (pDocView
, "style-updated", G_CALLBACK(onStyleContextChanged
), nullptr);
2606 static void lok_doc_view_set_property (GObject
* object
, guint propId
, const GValue
*value
, GParamSpec
*pspec
)
2608 LOKDocView
* pDocView
= LOK_DOC_VIEW (object
);
2609 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2610 bool bDocPasswordEnabled
= priv
->m_nLOKFeatures
& LOK_FEATURE_DOCUMENT_PASSWORD
;
2611 bool bDocPasswordToModifyEnabled
= priv
->m_nLOKFeatures
& LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY
;
2612 bool bTiledAnnotationsEnabled
= !(priv
->m_nLOKFeatures
& LOK_FEATURE_NO_TILED_ANNOTATIONS
);
2617 priv
->m_aLOPath
= g_value_get_string (value
);
2619 case PROP_LO_UNIPOLL
:
2620 priv
->m_bUnipoll
= g_value_get_boolean (value
);
2622 case PROP_LO_POINTER
:
2623 priv
->m_pOffice
= static_cast<LibreOfficeKit
*>(g_value_get_pointer(value
));
2625 case PROP_USER_PROFILE_URL
:
2626 if (const gchar
* pUserProfile
= g_value_get_string(value
))
2627 priv
->m_aUserProfileURL
= pUserProfile
;
2630 priv
->m_aDocPath
= g_value_get_string (value
);
2632 case PROP_DOC_POINTER
:
2633 priv
->m_pDocument
= static_cast<LibreOfficeKitDocument
*>(g_value_get_pointer(value
));
2634 priv
->m_eDocumentType
= static_cast<LibreOfficeKitDocumentType
>(priv
->m_pDocument
->pClass
->getDocumentType(priv
->m_pDocument
));
2637 lok_doc_view_set_edit (pDocView
, g_value_get_boolean (value
));
2640 lok_doc_view_set_zoom (pDocView
, g_value_get_float (value
));
2642 case PROP_DOC_WIDTH
:
2643 priv
->m_nDocumentWidthTwips
= g_value_get_long (value
);
2645 case PROP_DOC_HEIGHT
:
2646 priv
->m_nDocumentHeightTwips
= g_value_get_long (value
);
2648 case PROP_DOC_PASSWORD
:
2649 if (bool(g_value_get_boolean (value
)) != bDocPasswordEnabled
)
2651 priv
->m_nLOKFeatures
= priv
->m_nLOKFeatures
^ LOK_FEATURE_DOCUMENT_PASSWORD
;
2652 priv
->m_pOffice
->pClass
->setOptionalFeatures(priv
->m_pOffice
, priv
->m_nLOKFeatures
);
2655 case PROP_DOC_PASSWORD_TO_MODIFY
:
2656 if ( bool(g_value_get_boolean (value
)) != bDocPasswordToModifyEnabled
)
2658 priv
->m_nLOKFeatures
= priv
->m_nLOKFeatures
^ LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY
;
2659 priv
->m_pOffice
->pClass
->setOptionalFeatures(priv
->m_pOffice
, priv
->m_nLOKFeatures
);
2662 case PROP_TILED_ANNOTATIONS
:
2663 if ( bool(g_value_get_boolean (value
)) != bTiledAnnotationsEnabled
)
2665 priv
->m_nLOKFeatures
= priv
->m_nLOKFeatures
^ LOK_FEATURE_NO_TILED_ANNOTATIONS
;
2666 priv
->m_pOffice
->pClass
->setOptionalFeatures(priv
->m_pOffice
, priv
->m_nLOKFeatures
);
2670 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, propId
, pspec
);
2674 static void lok_doc_view_get_property (GObject
* object
, guint propId
, GValue
*value
, GParamSpec
*pspec
)
2676 LOKDocView
* pDocView
= LOK_DOC_VIEW (object
);
2677 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2682 g_value_set_string (value
, priv
->m_aLOPath
.c_str());
2684 case PROP_LO_UNIPOLL
:
2685 g_value_set_boolean (value
, priv
->m_bUnipoll
);
2687 case PROP_LO_POINTER
:
2688 g_value_set_pointer(value
, priv
->m_pOffice
);
2690 case PROP_USER_PROFILE_URL
:
2691 g_value_set_string(value
, priv
->m_aUserProfileURL
.c_str());
2694 g_value_set_string (value
, priv
->m_aDocPath
.c_str());
2696 case PROP_DOC_POINTER
:
2697 g_value_set_pointer(value
, priv
->m_pDocument
);
2700 g_value_set_boolean (value
, priv
->m_bEdit
);
2702 case PROP_LOAD_PROGRESS
:
2703 g_value_set_double (value
, priv
->m_nLoadProgress
);
2706 g_value_set_float (value
, priv
->m_fZoom
);
2708 case PROP_IS_LOADING
:
2709 g_value_set_boolean (value
, priv
->m_bIsLoading
);
2711 case PROP_IS_INITIALIZED
:
2712 g_value_set_boolean (value
, priv
->m_bInit
);
2714 case PROP_DOC_WIDTH
:
2715 g_value_set_long (value
, priv
->m_nDocumentWidthTwips
);
2717 case PROP_DOC_HEIGHT
:
2718 g_value_set_long (value
, priv
->m_nDocumentHeightTwips
);
2720 case PROP_CAN_ZOOM_IN
:
2721 g_value_set_boolean (value
, priv
->m_bCanZoomIn
);
2723 case PROP_CAN_ZOOM_OUT
:
2724 g_value_set_boolean (value
, priv
->m_bCanZoomOut
);
2726 case PROP_DOC_PASSWORD
:
2727 g_value_set_boolean (value
, (priv
->m_nLOKFeatures
& LOK_FEATURE_DOCUMENT_PASSWORD
) != 0);
2729 case PROP_DOC_PASSWORD_TO_MODIFY
:
2730 g_value_set_boolean (value
, (priv
->m_nLOKFeatures
& LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY
) != 0);
2732 case PROP_TILED_ANNOTATIONS
:
2733 g_value_set_boolean (value
, !(priv
->m_nLOKFeatures
& LOK_FEATURE_NO_TILED_ANNOTATIONS
));
2736 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, propId
, pspec
);
2740 static gboolean
lok_doc_view_draw (GtkWidget
* pWidget
, cairo_t
* pCairo
)
2742 LOKDocView
*pDocView
= LOK_DOC_VIEW (pWidget
);
2744 renderDocument (pDocView
, pCairo
);
2745 renderOverlay (pDocView
, pCairo
);
2750 //rhbz#1444437 finalize may not occur immediately when this widget is destroyed
2751 //it may happen during GC of javascript, e.g. in gnome-documents but "destroy"
2752 //will be called promptly, so close documents in destroy, not finalize
2753 static void lok_doc_view_destroy (GtkWidget
* widget
)
2755 LOKDocView
* pDocView
= LOK_DOC_VIEW (widget
);
2756 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2758 // Ignore notifications sent to this view on shutdown.
2759 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2760 if (priv
->m_pDocument
)
2762 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
2763 priv
->m_pDocument
->pClass
->registerCallback(priv
->m_pDocument
, nullptr, nullptr);
2766 if (priv
->lokThreadPool
)
2768 g_thread_pool_free(priv
->lokThreadPool
, true, true);
2769 priv
->lokThreadPool
= nullptr;
2774 if (priv
->m_pDocument
)
2776 // This call may drop several views - e.g., embedded OLE in-place clients
2777 priv
->m_pDocument
->pClass
->destroyView(priv
->m_pDocument
, priv
->m_nViewId
);
2778 if (priv
->m_pDocument
->pClass
->getViewsCount(priv
->m_pDocument
) == 0)
2780 // Last view(s) gone
2781 priv
->m_pDocument
->pClass
->destroy (priv
->m_pDocument
);
2782 priv
->m_pDocument
= nullptr;
2783 if (priv
->m_pOffice
)
2785 priv
->m_pOffice
->pClass
->destroy (priv
->m_pOffice
);
2786 priv
->m_pOffice
= nullptr;
2791 GTK_WIDGET_CLASS (lok_doc_view_parent_class
)->destroy (widget
);
2794 static void lok_doc_view_finalize (GObject
* object
)
2796 LOKDocView
* pDocView
= LOK_DOC_VIEW (object
);
2797 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2799 delete priv
.m_pImpl
;
2800 priv
.m_pImpl
= nullptr;
2802 G_OBJECT_CLASS (lok_doc_view_parent_class
)->finalize (object
);
2805 // kicks the mainloop awake
2806 static gboolean
timeout_wakeup(void *)
2811 // integrate our mainloop with LOK's
2812 static int lok_poll_callback(void*, int timeoutUs
)
2816 guint timeout
= g_timeout_add(timeoutUs
/ 1000, timeout_wakeup
, nullptr);
2817 g_main_context_iteration(nullptr, true);
2818 g_source_remove(timeout
);
2821 g_main_context_iteration(nullptr, FALSE
);
2826 // thread-safe wakeup of our mainloop
2827 static void lok_wake_callback(void *)
2829 g_main_context_wakeup(nullptr);
2832 static gboolean
spin_lok_loop(void *pData
)
2834 LOKDocView
*pDocView
= LOK_DOC_VIEW (pData
);
2835 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2836 priv
->m_pOffice
->pClass
->runLoop(priv
->m_pOffice
, lok_poll_callback
, lok_wake_callback
, nullptr);
2840 // Update the client's view size
2841 static void updateClientZoom(LOKDocView
*pDocView
)
2843 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2845 return; // Not initialized yet?
2846 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
2847 gint nTileSizePixelsScaled
= nTileSizePixels
* nScaleFactor
;
2848 GError
* error
= nullptr;
2850 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
2851 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_CLIENT_ZOOM
);
2852 pLOEvent
->m_nTilePixelWidth
= nTileSizePixelsScaled
;
2853 pLOEvent
->m_nTilePixelHeight
= nTileSizePixelsScaled
;
2854 pLOEvent
->m_nTileTwipWidth
= pixelToTwip(nTileSizePixelsScaled
, priv
->m_fZoom
* nScaleFactor
);
2855 pLOEvent
->m_nTileTwipHeight
= pixelToTwip(nTileSizePixelsScaled
, priv
->m_fZoom
* nScaleFactor
);
2856 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
2858 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
2859 if (error
!= nullptr)
2861 g_warning("Unable to call LOK_SET_CLIENT_ZOOM: %s", error
->message
);
2862 g_clear_error(&error
);
2864 g_object_unref(task
);
2866 priv
->m_nTileSizeTwips
= pixelToTwip(nTileSizePixelsScaled
, priv
->m_fZoom
* nScaleFactor
);
2869 static gboolean
lok_doc_view_initable_init (GInitable
*initable
, GCancellable
* /*cancellable*/, GError
**error
)
2871 LOKDocView
*pDocView
= LOK_DOC_VIEW (initable
);
2872 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2874 if (priv
->m_pOffice
!= nullptr)
2877 if (priv
->m_bUnipoll
)
2878 (void)g_setenv("SAL_LOK_OPTIONS", "unipoll", FALSE
);
2880 static const char testingLangs
[] = "de_DE en_GB en_US es_ES fr_FR it nl pt_BR pt_PT ru";
2881 (void)g_setenv("LOK_ALLOWLIST_LANGUAGES", testingLangs
, FALSE
);
2883 priv
->m_pOffice
= lok_init_2(priv
->m_aLOPath
.c_str(), priv
->m_aUserProfileURL
.empty() ? nullptr : priv
->m_aUserProfileURL
.c_str());
2885 if (priv
->m_pOffice
== nullptr)
2888 g_quark_from_static_string ("LOK initialization error"), 0,
2889 "Failed to get LibreOfficeKit context. Make sure path (%s) is correct",
2890 priv
->m_aLOPath
.c_str());
2893 priv
->m_nLOKFeatures
|= LOK_FEATURE_PART_IN_INVALIDATION_CALLBACK
;
2894 priv
->m_nLOKFeatures
|= LOK_FEATURE_VIEWID_IN_VISCURSOR_INVALIDATION_CALLBACK
;
2895 priv
->m_pOffice
->pClass
->setOptionalFeatures(priv
->m_pOffice
, priv
->m_nLOKFeatures
);
2897 if (priv
->m_bUnipoll
)
2898 g_idle_add(spin_lok_loop
, pDocView
);
2903 static void lok_doc_view_initable_iface_init (GInitableIface
*iface
)
2905 iface
->init
= lok_doc_view_initable_init
;
2908 static void lok_doc_view_class_init (LOKDocViewClass
* pClass
)
2910 GObjectClass
*pGObjectClass
= G_OBJECT_CLASS(pClass
);
2911 GtkWidgetClass
*pWidgetClass
= GTK_WIDGET_CLASS(pClass
);
2913 pGObjectClass
->get_property
= lok_doc_view_get_property
;
2914 pGObjectClass
->set_property
= lok_doc_view_set_property
;
2915 pGObjectClass
->finalize
= lok_doc_view_finalize
;
2917 pWidgetClass
->draw
= lok_doc_view_draw
;
2918 pWidgetClass
->button_press_event
= lok_doc_view_signal_button
;
2919 pWidgetClass
->button_release_event
= lok_doc_view_signal_button
;
2920 pWidgetClass
->key_press_event
= signalKey
;
2921 pWidgetClass
->key_release_event
= signalKey
;
2922 pWidgetClass
->motion_notify_event
= lok_doc_view_signal_motion
;
2923 pWidgetClass
->destroy
= lok_doc_view_destroy
;
2926 * LOKDocView:lopath:
2928 * The absolute path of the LibreOffice install.
2930 properties
[PROP_LO_PATH
] =
2931 g_param_spec_string("lopath",
2933 "LibreOffice Install Path",
2935 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2936 G_PARAM_CONSTRUCT_ONLY
|
2937 G_PARAM_STATIC_STRINGS
));
2940 * LOKDocView:unipoll:
2942 * Whether we use our own unified polling mainloop in place of glib's
2944 properties
[PROP_LO_UNIPOLL
] =
2945 g_param_spec_boolean("unipoll",
2947 "Whether we use a custom unified polling loop",
2949 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2950 G_PARAM_CONSTRUCT_ONLY
|
2951 G_PARAM_STATIC_STRINGS
));
2953 * LOKDocView:lopointer:
2955 * A LibreOfficeKit* in case lok_init() is already called
2958 properties
[PROP_LO_POINTER
] =
2959 g_param_spec_pointer("lopointer",
2961 "A LibreOfficeKit* from lok_init()",
2962 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2963 G_PARAM_CONSTRUCT_ONLY
|
2964 G_PARAM_STATIC_STRINGS
));
2967 * LOKDocView:userprofileurl:
2969 * The absolute path of the LibreOffice user profile.
2971 properties
[PROP_USER_PROFILE_URL
] =
2972 g_param_spec_string("userprofileurl",
2973 "User profile path",
2974 "LibreOffice user profile path",
2976 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2977 G_PARAM_CONSTRUCT_ONLY
|
2978 G_PARAM_STATIC_STRINGS
));
2981 * LOKDocView:docpath:
2983 * The path of the document that is currently being viewed.
2985 properties
[PROP_DOC_PATH
] =
2986 g_param_spec_string("docpath",
2988 "The URI of the document to open",
2990 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2991 G_PARAM_STATIC_STRINGS
));
2994 * LOKDocView:docpointer:
2996 * A LibreOfficeKitDocument* in case documentLoad() is already called
2999 properties
[PROP_DOC_POINTER
] =
3000 g_param_spec_pointer("docpointer",
3002 "A LibreOfficeKitDocument* from documentLoad()",
3003 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
3004 G_PARAM_STATIC_STRINGS
));
3007 * LOKDocView:editable:
3009 * Whether the document loaded inside of #LOKDocView is editable or not.
3011 properties
[PROP_EDITABLE
] =
3012 g_param_spec_boolean("editable",
3014 "Whether the content is in edit mode or not",
3016 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
3017 G_PARAM_STATIC_STRINGS
));
3020 * LOKDocView:load-progress:
3022 * The percent completion of the current loading operation of the
3023 * document. This can be used for progress bars. Note that this is not a
3024 * very accurate progress indicator, and its value might reset it couple of
3025 * times to 0 and start again. You should not rely on its numbers.
3027 properties
[PROP_LOAD_PROGRESS
] =
3028 g_param_spec_double("load-progress",
3029 "Estimated Load Progress",
3030 "Shows the progress of the document load operation",
3032 static_cast<GParamFlags
>(G_PARAM_READABLE
|
3033 G_PARAM_STATIC_STRINGS
));
3036 * LOKDocView:zoom-level:
3038 * The current zoom level of the document loaded inside #LOKDocView. The
3039 * default value is 1.0.
3041 properties
[PROP_ZOOM
] =
3042 g_param_spec_float("zoom-level",
3044 "The current zoom level of the content",
3046 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
3047 G_PARAM_STATIC_STRINGS
));
3050 * LOKDocView:is-loading:
3052 * Whether the requested document is being loaded or not. %TRUE if it is
3053 * being loaded, otherwise %FALSE.
3055 properties
[PROP_IS_LOADING
] =
3056 g_param_spec_boolean("is-loading",
3058 "Whether the view is loading a document",
3060 static_cast<GParamFlags
>(G_PARAM_READABLE
|
3061 G_PARAM_STATIC_STRINGS
));
3064 * LOKDocView:is-initialized:
3066 * Whether the requested document has completely loaded or not.
3068 properties
[PROP_IS_INITIALIZED
] =
3069 g_param_spec_boolean("is-initialized",
3071 "Whether the view has completely initialized",
3073 static_cast<GParamFlags
>(G_PARAM_READABLE
|
3074 G_PARAM_STATIC_STRINGS
));
3077 * LOKDocView:doc-width:
3079 * The width of the currently loaded document in #LOKDocView in twips.
3081 properties
[PROP_DOC_WIDTH
] =
3082 g_param_spec_long("doc-width",
3084 "Width of the document in twips",
3086 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
3087 G_PARAM_STATIC_STRINGS
));
3090 * LOKDocView:doc-height:
3092 * The height of the currently loaded document in #LOKDocView in twips.
3094 properties
[PROP_DOC_HEIGHT
] =
3095 g_param_spec_long("doc-height",
3097 "Height of the document in twips",
3099 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
3100 G_PARAM_STATIC_STRINGS
));
3103 * LOKDocView:can-zoom-in:
3105 * It tells whether the view can further be zoomed in or not.
3107 properties
[PROP_CAN_ZOOM_IN
] =
3108 g_param_spec_boolean("can-zoom-in",
3110 "Whether the view can be zoomed in further",
3112 static_cast<GParamFlags
>(G_PARAM_READABLE
3113 | G_PARAM_STATIC_STRINGS
));
3116 * LOKDocView:can-zoom-out:
3118 * It tells whether the view can further be zoomed out or not.
3120 properties
[PROP_CAN_ZOOM_OUT
] =
3121 g_param_spec_boolean("can-zoom-out",
3123 "Whether the view can be zoomed out further",
3125 static_cast<GParamFlags
>(G_PARAM_READABLE
3126 | G_PARAM_STATIC_STRINGS
));
3129 * LOKDocView:doc-password:
3131 * Set it to true if client supports providing password for viewing
3132 * password protected documents
3134 properties
[PROP_DOC_PASSWORD
] =
3135 g_param_spec_boolean("doc-password",
3136 "Document password capability",
3137 "Whether client supports providing document passwords",
3139 static_cast<GParamFlags
>(G_PARAM_READWRITE
3140 | G_PARAM_STATIC_STRINGS
));
3143 * LOKDocView:doc-password-to-modify:
3145 * Set it to true if client supports providing password for edit-protected documents
3147 properties
[PROP_DOC_PASSWORD_TO_MODIFY
] =
3148 g_param_spec_boolean("doc-password-to-modify",
3149 "Edit document password capability",
3150 "Whether the client supports providing passwords to edit documents",
3152 static_cast<GParamFlags
>(G_PARAM_READWRITE
3153 | G_PARAM_STATIC_STRINGS
));
3156 * LOKDocView:tiled-annotations-rendering:
3158 * Set it to false if client does not want LO to render comments in tiles and
3159 * instead interested in using comments API to access comments
3161 properties
[PROP_TILED_ANNOTATIONS
] =
3162 g_param_spec_boolean("tiled-annotations",
3163 "Render comments in tiles",
3164 "Whether the client wants in tile comment rendering",
3166 static_cast<GParamFlags
>(G_PARAM_READWRITE
3167 | G_PARAM_STATIC_STRINGS
));
3169 g_object_class_install_properties(pGObjectClass
, PROP_LAST
, properties
);
3172 * LOKDocView::load-changed:
3173 * @pDocView: the #LOKDocView on which the signal is emitted
3174 * @fLoadProgress: the new progress value
3176 doc_view_signals
[LOAD_CHANGED
] =
3177 g_signal_new("load-changed",
3178 G_TYPE_FROM_CLASS (pGObjectClass
),
3182 g_cclosure_marshal_VOID__DOUBLE
,
3187 * LOKDocView::edit-changed:
3188 * @pDocView: the #LOKDocView on which the signal is emitted
3189 * @bEdit: the new edit value of the view
3191 doc_view_signals
[EDIT_CHANGED
] =
3192 g_signal_new("edit-changed",
3193 G_TYPE_FROM_CLASS (pGObjectClass
),
3197 g_cclosure_marshal_VOID__BOOLEAN
,
3202 * LOKDocView::command-changed:
3203 * @pDocView: the #LOKDocView on which the signal is emitted
3204 * @aCommand: the command that was changed
3206 doc_view_signals
[COMMAND_CHANGED
] =
3207 g_signal_new("command-changed",
3208 G_TYPE_FROM_CLASS(pGObjectClass
),
3212 g_cclosure_marshal_VOID__STRING
,
3217 * LOKDocView::search-not-found:
3218 * @pDocView: the #LOKDocView on which the signal is emitted
3219 * @aCommand: the string for which the search was not found.
3221 doc_view_signals
[SEARCH_NOT_FOUND
] =
3222 g_signal_new("search-not-found",
3223 G_TYPE_FROM_CLASS(pGObjectClass
),
3227 g_cclosure_marshal_VOID__STRING
,
3232 * LOKDocView::part-changed:
3233 * @pDocView: the #LOKDocView on which the signal is emitted
3234 * @aCommand: the part number which the view changed to
3236 doc_view_signals
[PART_CHANGED
] =
3237 g_signal_new("part-changed",
3238 G_TYPE_FROM_CLASS(pGObjectClass
),
3242 g_cclosure_marshal_VOID__INT
,
3247 * LOKDocView::size-changed:
3248 * @pDocView: the #LOKDocView on which the signal is emitted
3249 * @aCommand: NULL, we just notify that want to notify the UI elements that are interested.
3251 doc_view_signals
[SIZE_CHANGED
] =
3252 g_signal_new("size-changed",
3253 G_TYPE_FROM_CLASS(pGObjectClass
),
3257 g_cclosure_marshal_VOID__VOID
,
3262 * LOKDocView::hyperlinked-clicked:
3263 * @pDocView: the #LOKDocView on which the signal is emitted
3264 * @aHyperlink: the URI which the application should handle
3266 doc_view_signals
[HYPERLINK_CLICKED
] =
3267 g_signal_new("hyperlink-clicked",
3268 G_TYPE_FROM_CLASS(pGObjectClass
),
3272 g_cclosure_marshal_VOID__STRING
,
3277 * LOKDocView::cursor-changed:
3278 * @pDocView: the #LOKDocView on which the signal is emitted
3279 * @nX: The new cursor position (X coordinate) in pixels
3280 * @nY: The new cursor position (Y coordinate) in pixels
3281 * @nWidth: The width of new cursor
3282 * @nHeight: The height of new cursor
3284 doc_view_signals
[CURSOR_CHANGED
] =
3285 g_signal_new("cursor-changed",
3286 G_TYPE_FROM_CLASS(pGObjectClass
),
3290 g_cclosure_marshal_generic
,
3292 G_TYPE_INT
, G_TYPE_INT
,
3293 G_TYPE_INT
, G_TYPE_INT
);
3296 * LOKDocView::search-result-count:
3297 * @pDocView: the #LOKDocView on which the signal is emitted
3298 * @aCommand: number of matches.
3300 doc_view_signals
[SEARCH_RESULT_COUNT
] =
3301 g_signal_new("search-result-count",
3302 G_TYPE_FROM_CLASS(pGObjectClass
),
3306 g_cclosure_marshal_VOID__STRING
,
3311 * LOKDocView::command-result:
3312 * @pDocView: the #LOKDocView on which the signal is emitted
3313 * @aCommand: JSON containing the info about the command that finished,
3314 * and its success status.
3316 doc_view_signals
[COMMAND_RESULT
] =
3317 g_signal_new("command-result",
3318 G_TYPE_FROM_CLASS(pGObjectClass
),
3322 g_cclosure_marshal_VOID__STRING
,
3327 * LOKDocView::address-changed:
3328 * @pDocView: the #LOKDocView on which the signal is emitted
3329 * @aCommand: formula text content
3331 doc_view_signals
[ADDRESS_CHANGED
] =
3332 g_signal_new("address-changed",
3333 G_TYPE_FROM_CLASS(pGObjectClass
),
3337 g_cclosure_marshal_VOID__STRING
,
3342 * LOKDocView::formula-changed:
3343 * @pDocView: the #LOKDocView on which the signal is emitted
3344 * @aCommand: formula text content
3346 doc_view_signals
[FORMULA_CHANGED
] =
3347 g_signal_new("formula-changed",
3348 G_TYPE_FROM_CLASS(pGObjectClass
),
3352 g_cclosure_marshal_VOID__STRING
,
3357 * LOKDocView::text-selection:
3358 * @pDocView: the #LOKDocView on which the signal is emitted
3359 * @bIsTextSelected: whether text selected is non-null
3361 doc_view_signals
[TEXT_SELECTION
] =
3362 g_signal_new("text-selection",
3363 G_TYPE_FROM_CLASS(pGObjectClass
),
3367 g_cclosure_marshal_VOID__BOOLEAN
,
3372 * LOKDocView::content-control:
3373 * @pDocView: the #LOKDocView on which the signal is emitted
3374 * @pPayload: the JSON string containing the information about ruler properties
3376 doc_view_signals
[CONTENT_CONTROL
] =
3377 g_signal_new("content-control",
3378 G_TYPE_FROM_CLASS(pGObjectClass
),
3382 g_cclosure_marshal_generic
,
3387 * LOKDocView::password-required:
3388 * @pDocView: the #LOKDocView on which the signal is emitted
3389 * @pUrl: URL of the document for which password is required
3390 * @bModify: whether password id required to modify the document
3391 * This is true when password is required to edit the document,
3392 * while it can still be viewed without password. In such cases, provide a NULL
3393 * password for read-only access to the document.
3394 * If false, password is required for opening the document, and document
3395 * cannot be opened without providing a valid password.
3397 * Password must be provided by calling lok_doc_view_set_document_password
3398 * function with pUrl as provided by the callback.
3400 * Upon entering an invalid password, another `password-required` signal is
3402 * Upon entering a valid password, document starts to load.
3403 * Upon entering a NULL password: if bModify is %TRUE, document starts to
3404 * open in view-only mode, else loading of document is aborted.
3406 doc_view_signals
[PASSWORD_REQUIRED
] =
3407 g_signal_new("password-required",
3408 G_TYPE_FROM_CLASS(pGObjectClass
),
3412 g_cclosure_marshal_generic
,
3418 * LOKDocView::comment:
3419 * @pDocView: the #LOKDocView on which the signal is emitted
3420 * @pComment: the JSON string containing comment notification
3421 * The has following structure containing the information telling whether
3422 * the comment has been added, deleted or modified.
3429 * "author": "Unknown Author",
3430 * "text": "This is a comment",
3431 * "dateTime": "2016-08-18T13:13:00",
3432 * "anchorPos": "4529, 3906",
3433 * "textRange": "1418, 3906, 3111, 919"
3436 * 'action' can be 'Add', 'Remove' or 'Modify' depending on whether
3437 * comment has been added, removed or modified.
3438 * 'parent' is a non-zero comment id if this comment is a reply comment,
3439 * otherwise it's a root comment.
3441 doc_view_signals
[COMMENT
] =
3442 g_signal_new("comment",
3443 G_TYPE_FROM_CLASS(pGObjectClass
),
3447 g_cclosure_marshal_generic
,
3452 * LOKDocView::ruler:
3453 * @pDocView: the #LOKDocView on which the signal is emitted
3454 * @pPayload: the JSON string containing the information about ruler properties
3456 * The payload format is:
3461 * "leftOffset": "...",
3462 * "pageOffset": "...",
3463 * "pageWidth": "...",
3467 doc_view_signals
[RULER
] =
3468 g_signal_new("ruler",
3469 G_TYPE_FROM_CLASS(pGObjectClass
),
3473 g_cclosure_marshal_generic
,
3478 * LOKDocView::window::
3479 * @pDocView: the #LOKDocView on which the signal is emitted
3480 * @pPayload: the JSON string containing the information about the window
3482 * This signal emits information about external windows like dialogs, autopopups for now.
3484 * The payload format of pPayload is:
3487 * "id": "unique integer id of the dialog",
3488 * "action": "<see below>",
3489 * "type": "<see below>"
3490 * "rectangle": "x, y, width, height"
3493 * "type" tells the type of the window the action is associated with
3494 * - "dialog" - window is a dialog
3495 * - "child" - window is a floating window (combo boxes, etc.)
3497 * "action" can take following values:
3498 * - "created" - window is created in the backend, client can render it now
3499 * - "title_changed" - window's title is changed
3500 * - "size_changed" - window's size is changed
3501 * - "invalidate" - the area as described by "rectangle" is invalidated
3502 * Clients must request the new area
3503 * - "cursor_invalidate" - cursor is invalidated. New position is in "rectangle"
3504 * - "cursor_visible" - cursor visible status is changed. Status is available
3505 * in "visible" field
3506 * - "close" - window is closed
3508 doc_view_signals
[WINDOW
] =
3509 g_signal_new("window",
3510 G_TYPE_FROM_CLASS(pGObjectClass
),
3514 g_cclosure_marshal_generic
,
3519 * LOKDocView::invalidate-header::
3520 * @pDocView: the #LOKDocView on which the signal is emitted
3521 * @pPayload: can be either "row", "column", or "all".
3523 * The column/row header is no more valid because of a column/row insertion
3524 * or a similar event. Clients must query a new column/row header set.
3526 * The payload says if we are invalidating a row or column header
3528 doc_view_signals
[INVALIDATE_HEADER
] =
3529 g_signal_new("invalidate-header",
3530 G_TYPE_FROM_CLASS(pGObjectClass
),
3534 g_cclosure_marshal_generic
,
3539 SAL_DLLPUBLIC_EXPORT GtkWidget
*
3540 lok_doc_view_new (const gchar
* pPath
, GCancellable
*cancellable
, GError
**error
)
3542 return GTK_WIDGET (g_initable_new (LOK_TYPE_DOC_VIEW
, cancellable
, error
,
3543 "lopath", pPath
== nullptr ? LOK_PATH
: pPath
,
3544 "halign", GTK_ALIGN_CENTER
,
3545 "valign", GTK_ALIGN_CENTER
,
3549 SAL_DLLPUBLIC_EXPORT GtkWidget
*
3550 lok_doc_view_new_from_user_profile (const gchar
* pPath
, const gchar
* pUserProfile
, GCancellable
*cancellable
, GError
**error
)
3552 return GTK_WIDGET(g_initable_new(LOK_TYPE_DOC_VIEW
, cancellable
, error
,
3553 "lopath", pPath
== nullptr ? LOK_PATH
: pPath
,
3554 "userprofileurl", pUserProfile
,
3555 "halign", GTK_ALIGN_CENTER
,
3556 "valign", GTK_ALIGN_CENTER
,
3560 SAL_DLLPUBLIC_EXPORT GtkWidget
* lok_doc_view_new_from_widget(LOKDocView
* pOldLOKDocView
,
3561 const gchar
* pRenderingArguments
)
3563 LOKDocViewPrivate
& pOldPriv
= getPrivate(pOldLOKDocView
);
3564 GtkWidget
* pNewDocView
= GTK_WIDGET(g_initable_new(LOK_TYPE_DOC_VIEW
, /*cancellable=*/nullptr, /*error=*/nullptr,
3565 "lopath", pOldPriv
->m_aLOPath
.c_str(),
3566 "userprofileurl", pOldPriv
->m_aUserProfileURL
.c_str(),
3567 "lopointer", pOldPriv
->m_pOffice
,
3568 "docpointer", pOldPriv
->m_pDocument
,
3569 "halign", GTK_ALIGN_CENTER
,
3570 "valign", GTK_ALIGN_CENTER
,
3573 // No documentLoad(), just a createView().
3574 LibreOfficeKitDocument
* pDocument
= lok_doc_view_get_document(LOK_DOC_VIEW(pNewDocView
));
3575 LOKDocViewPrivate
& pNewPriv
= getPrivate(LOK_DOC_VIEW(pNewDocView
));
3576 // Store the view id only later in postDocumentLoad(), as
3577 // initializeForRendering() changes the id in Impress.
3578 pDocument
->pClass
->createView(pDocument
);
3579 pNewPriv
->m_aRenderingArguments
= pRenderingArguments
;
3581 postDocumentLoad(pNewDocView
);
3585 SAL_DLLPUBLIC_EXPORT gboolean
3586 lok_doc_view_open_document_finish (LOKDocView
* pDocView
, GAsyncResult
* res
, GError
** error
)
3588 GTask
* task
= G_TASK(res
);
3590 g_return_val_if_fail(g_task_is_valid(res
, pDocView
), false);
3591 g_return_val_if_fail(g_task_get_source_tag(task
) == lok_doc_view_open_document
, false);
3592 g_return_val_if_fail(error
== nullptr || *error
== nullptr, false);
3594 return g_task_propagate_boolean(task
, error
);
3597 SAL_DLLPUBLIC_EXPORT
void
3598 lok_doc_view_open_document (LOKDocView
* pDocView
,
3600 const gchar
* pRenderingArguments
,
3601 GCancellable
* cancellable
,
3602 GAsyncReadyCallback callback
,
3605 GTask
* task
= g_task_new(pDocView
, cancellable
, callback
, userdata
);
3606 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3607 GError
* error
= nullptr;
3609 LOEvent
* pLOEvent
= new LOEvent(LOK_LOAD_DOC
);
3611 g_object_set(G_OBJECT(pDocView
), "docpath", pPath
, nullptr);
3612 if (pRenderingArguments
)
3613 priv
->m_aRenderingArguments
= pRenderingArguments
;
3614 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
3615 g_task_set_source_tag(task
, reinterpret_cast<gpointer
>(lok_doc_view_open_document
));
3617 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
3618 if (error
!= nullptr)
3620 g_warning("Unable to call LOK_LOAD_DOC: %s", error
->message
);
3621 g_clear_error(&error
);
3623 g_object_unref(task
);
3626 SAL_DLLPUBLIC_EXPORT LibreOfficeKitDocument
*
3627 lok_doc_view_get_document (LOKDocView
* pDocView
)
3629 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3630 return priv
->m_pDocument
;
3633 SAL_DLLPUBLIC_EXPORT
void
3634 lok_doc_view_set_visible_area (LOKDocView
* pDocView
, GdkRectangle
* pVisibleArea
)
3639 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3640 priv
->m_aVisibleArea
= *pVisibleArea
;
3641 priv
->m_bVisibleAreaSet
= true;
3645 // This used to be rtl::math::approxEqual() but since that isn't inline anymore
3646 // in rtl/math.hxx and was moved into libuno_sal as rtl_math_approxEqual() to
3647 // cater for representable integer cases and we don't want to link against
3648 // libuno_sal, we'll have to have an own implementation. The special large
3649 // integer cases seems not be needed here.
3650 bool lok_approxEqual(double a
, double b
)
3652 static const double e48
= 1.0 / (16777216.0 * 16777216.0);
3655 if (a
== 0.0 || b
== 0.0)
3657 const double d
= fabs(a
- b
);
3658 return (d
< fabs(a
) * e48
&& d
< fabs(b
) * e48
);
3662 SAL_DLLPUBLIC_EXPORT
void
3663 lok_doc_view_set_zoom (LOKDocView
* pDocView
, float fZoom
)
3665 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3667 if (!priv
->m_pDocument
)
3670 // Clamp the input value in [MIN_ZOOM, MAX_ZOOM]
3671 fZoom
= fZoom
< MIN_ZOOM
? MIN_ZOOM
: fZoom
;
3672 fZoom
= std::min(fZoom
, MAX_ZOOM
);
3674 if (lok_approxEqual(fZoom
, priv
->m_fZoom
))
3677 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
3678 gint nTileSizePixelsScaled
= nTileSizePixels
* nScaleFactor
;
3679 priv
->m_fZoom
= fZoom
;
3680 long nDocumentWidthPixels
= twipToPixel(priv
->m_nDocumentWidthTwips
, fZoom
* nScaleFactor
);
3681 long nDocumentHeightPixels
= twipToPixel(priv
->m_nDocumentHeightTwips
, fZoom
* nScaleFactor
);
3682 // Total number of columns in this document.
3683 guint nColumns
= ceil(static_cast<double>(nDocumentWidthPixels
) / nTileSizePixelsScaled
);
3684 priv
->m_pTileBuffer
= std::make_unique
<TileBuffer
>(nColumns
, nScaleFactor
);
3685 gtk_widget_set_size_request(GTK_WIDGET(pDocView
),
3686 nDocumentWidthPixels
/ nScaleFactor
,
3687 nDocumentHeightPixels
/ nScaleFactor
);
3689 g_object_notify_by_pspec(G_OBJECT(pDocView
), properties
[PROP_ZOOM
]);
3691 // set properties to indicate if view can be further zoomed in/out
3692 bool bCanZoomIn
= priv
->m_fZoom
< MAX_ZOOM
;
3693 bool bCanZoomOut
= priv
->m_fZoom
> MIN_ZOOM
;
3694 if (bCanZoomIn
!= bool(priv
->m_bCanZoomIn
))
3696 priv
->m_bCanZoomIn
= bCanZoomIn
;
3697 g_object_notify_by_pspec(G_OBJECT(pDocView
), properties
[PROP_CAN_ZOOM_IN
]);
3699 if (bCanZoomOut
!= bool(priv
->m_bCanZoomOut
))
3701 priv
->m_bCanZoomOut
= bCanZoomOut
;
3702 g_object_notify_by_pspec(G_OBJECT(pDocView
), properties
[PROP_CAN_ZOOM_OUT
]);
3705 updateClientZoom(pDocView
);
3708 SAL_DLLPUBLIC_EXPORT gfloat
3709 lok_doc_view_get_zoom (LOKDocView
* pDocView
)
3711 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3712 return priv
->m_fZoom
;
3715 SAL_DLLPUBLIC_EXPORT gint
3716 lok_doc_view_get_parts (LOKDocView
* pDocView
)
3718 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3719 if (!priv
->m_pDocument
)
3722 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
3723 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
3724 return priv
->m_pDocument
->pClass
->getParts( priv
->m_pDocument
);
3727 SAL_DLLPUBLIC_EXPORT gint
3728 lok_doc_view_get_part (LOKDocView
* pDocView
)
3730 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3731 if (!priv
->m_pDocument
)
3734 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
3735 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
3736 return priv
->m_pDocument
->pClass
->getPart( priv
->m_pDocument
);
3739 SAL_DLLPUBLIC_EXPORT
void
3740 lok_doc_view_set_part (LOKDocView
* pDocView
, int nPart
)
3742 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3743 if (!priv
->m_pDocument
)
3746 if (nPart
< 0 || nPart
>= priv
->m_nParts
)
3748 g_warning("Invalid part request : %d", nPart
);
3752 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
3753 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_PART
);
3754 GError
* error
= nullptr;
3756 pLOEvent
->m_nPart
= nPart
;
3757 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
3759 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
3760 if (error
!= nullptr)
3762 g_warning("Unable to call LOK_SET_PART: %s", error
->message
);
3763 g_clear_error(&error
);
3765 g_object_unref(task
);
3766 priv
->m_nPartId
= nPart
;
3769 SAL_DLLPUBLIC_EXPORT
void lok_doc_view_send_content_control_event(LOKDocView
* pDocView
,
3770 const gchar
* pArguments
)
3772 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3773 if (!priv
->m_pDocument
)
3778 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
3779 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
3780 return priv
->m_pDocument
->pClass
->sendContentControlEvent(priv
->m_pDocument
, pArguments
);
3783 SAL_DLLPUBLIC_EXPORT gchar
*
3784 lok_doc_view_get_part_name (LOKDocView
* pDocView
, int nPart
)
3786 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3787 if (!priv
->m_pDocument
)
3790 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
3791 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
3792 return priv
->m_pDocument
->pClass
->getPartName( priv
->m_pDocument
, nPart
);
3795 SAL_DLLPUBLIC_EXPORT
void
3796 lok_doc_view_set_partmode(LOKDocView
* pDocView
,
3799 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3800 if (!priv
->m_pDocument
)
3803 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
3804 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_PARTMODE
);
3805 GError
* error
= nullptr;
3807 pLOEvent
->m_nPartMode
= nPartMode
;
3808 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
3810 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
3811 if (error
!= nullptr)
3813 g_warning("Unable to call LOK_SET_PARTMODE: %s", error
->message
);
3814 g_clear_error(&error
);
3816 g_object_unref(task
);
3819 SAL_DLLPUBLIC_EXPORT
void
3820 lok_doc_view_reset_view(LOKDocView
* pDocView
)
3822 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3824 if (priv
->m_pTileBuffer
!= nullptr)
3825 priv
->m_pTileBuffer
->resetAllTiles();
3826 priv
->m_nLoadProgress
= 0.0;
3828 memset(&priv
->m_aVisibleCursor
, 0, sizeof(priv
->m_aVisibleCursor
));
3829 priv
->m_bCursorOverlayVisible
= false;
3830 priv
->m_bCursorVisible
= false;
3832 priv
->m_nLastButtonPressTime
= 0;
3833 priv
->m_nLastButtonReleaseTime
= 0;
3834 priv
->m_aTextSelectionRectangles
.clear();
3835 priv
->m_aContentControlRectangles
.clear();
3837 memset(&priv
->m_aTextSelectionStart
, 0, sizeof(priv
->m_aTextSelectionStart
));
3838 memset(&priv
->m_aTextSelectionEnd
, 0, sizeof(priv
->m_aTextSelectionEnd
));
3839 memset(&priv
->m_aGraphicSelection
, 0, sizeof(priv
->m_aGraphicSelection
));
3840 priv
->m_bInDragGraphicSelection
= false;
3841 memset(&priv
->m_aCellCursor
, 0, sizeof(priv
->m_aCellCursor
));
3843 cairo_surface_destroy(priv
->m_pHandleStart
);
3844 priv
->m_pHandleStart
= nullptr;
3845 memset(&priv
->m_aHandleStartRect
, 0, sizeof(priv
->m_aHandleStartRect
));
3846 priv
->m_bInDragStartHandle
= false;
3848 cairo_surface_destroy(priv
->m_pHandleMiddle
);
3849 priv
->m_pHandleMiddle
= nullptr;
3850 memset(&priv
->m_aHandleMiddleRect
, 0, sizeof(priv
->m_aHandleMiddleRect
));
3851 priv
->m_bInDragMiddleHandle
= false;
3853 cairo_surface_destroy(priv
->m_pHandleEnd
);
3854 priv
->m_pHandleEnd
= nullptr;
3855 memset(&priv
->m_aHandleEndRect
, 0, sizeof(priv
->m_aHandleEndRect
));
3856 priv
->m_bInDragEndHandle
= false;
3858 memset(&priv
->m_aGraphicHandleRects
, 0, sizeof(priv
->m_aGraphicHandleRects
));
3859 memset(&priv
->m_bInDragGraphicHandles
, 0, sizeof(priv
->m_bInDragGraphicHandles
));
3861 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
3864 SAL_DLLPUBLIC_EXPORT
void
3865 lok_doc_view_set_edit(LOKDocView
* pDocView
,
3868 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3869 if (!priv
->m_pDocument
)
3872 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
3873 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_EDIT
);
3874 GError
* error
= nullptr;
3876 pLOEvent
->m_bEdit
= bEdit
;
3877 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
3879 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
3880 if (error
!= nullptr)
3882 g_warning("Unable to call LOK_SET_EDIT: %s", error
->message
);
3883 g_clear_error(&error
);
3885 g_object_unref(task
);
3888 SAL_DLLPUBLIC_EXPORT gboolean
3889 lok_doc_view_get_edit (LOKDocView
* pDocView
)
3891 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3892 return priv
->m_bEdit
;
3895 SAL_DLLPUBLIC_EXPORT
void
3896 lok_doc_view_post_command (LOKDocView
* pDocView
,
3897 const gchar
* pCommand
,
3898 const gchar
* pArguments
,
3899 gboolean bNotifyWhenFinished
)
3901 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3902 if (!priv
->m_pDocument
)
3906 LOKPostCommand(pDocView
, pCommand
, pArguments
, bNotifyWhenFinished
);
3908 g_info ("LOK_POST_COMMAND: ignoring commands in view-only mode");
3911 SAL_DLLPUBLIC_EXPORT gchar
*
3912 lok_doc_view_get_command_values (LOKDocView
* pDocView
,
3913 const gchar
* pCommand
)
3915 g_return_val_if_fail (LOK_IS_DOC_VIEW (pDocView
), nullptr);
3916 g_return_val_if_fail (pCommand
!= nullptr, nullptr);
3918 LibreOfficeKitDocument
* pDocument
= lok_doc_view_get_document(pDocView
);
3922 return pDocument
->pClass
->getCommandValues(pDocument
, pCommand
);
3925 SAL_DLLPUBLIC_EXPORT
void
3926 lok_doc_view_find_prev (LOKDocView
* pDocView
,
3928 gboolean bHighlightAll
)
3930 doSearch(pDocView
, pText
, true, bHighlightAll
);
3933 SAL_DLLPUBLIC_EXPORT
void
3934 lok_doc_view_find_next (LOKDocView
* pDocView
,
3936 gboolean bHighlightAll
)
3938 doSearch(pDocView
, pText
, false, bHighlightAll
);
3941 SAL_DLLPUBLIC_EXPORT
void
3942 lok_doc_view_highlight_all (LOKDocView
* pDocView
,
3945 doSearch(pDocView
, pText
, false, true);
3948 SAL_DLLPUBLIC_EXPORT gchar
*
3949 lok_doc_view_copy_selection (LOKDocView
* pDocView
,
3950 const gchar
* pMimeType
,
3951 gchar
** pUsedMimeType
)
3953 LibreOfficeKitDocument
* pDocument
= lok_doc_view_get_document(pDocView
);
3957 std::stringstream ss
;
3958 ss
<< "lok::Document::getTextSelection('" << pMimeType
<< "')";
3959 g_info("%s", ss
.str().c_str());
3960 return pDocument
->pClass
->getTextSelection(pDocument
, pMimeType
, pUsedMimeType
);
3963 SAL_DLLPUBLIC_EXPORT gboolean
3964 lok_doc_view_paste (LOKDocView
* pDocView
,
3965 const gchar
* pMimeType
,
3969 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3970 LibreOfficeKitDocument
* pDocument
= priv
->m_pDocument
;
3978 g_info ("ignoring paste in view-only mode");
3984 std::stringstream ss
;
3985 ss
<< "lok::Document::paste('" << pMimeType
<< "', '" << std::string(pData
, nSize
) << ", "<<nSize
<<"')";
3986 g_info("%s", ss
.str().c_str());
3987 ret
= pDocument
->pClass
->paste(pDocument
, pMimeType
, pData
, nSize
);
3993 SAL_DLLPUBLIC_EXPORT
void
3994 lok_doc_view_set_document_password (LOKDocView
* pDocView
,
3996 const gchar
* pPassword
)
3998 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
4000 priv
->m_pOffice
->pClass
->setDocumentPassword(priv
->m_pOffice
, pURL
, pPassword
);
4003 SAL_DLLPUBLIC_EXPORT gchar
*
4004 lok_doc_view_get_version_info (LOKDocView
* pDocView
)
4006 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
4008 return priv
->m_pOffice
->pClass
->getVersionInfo(priv
->m_pOffice
);
4012 SAL_DLLPUBLIC_EXPORT gfloat
4013 lok_doc_view_pixel_to_twip (LOKDocView
* pDocView
, float fInput
)
4015 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
4016 return pixelToTwip(fInput
, priv
->m_fZoom
);
4019 SAL_DLLPUBLIC_EXPORT gfloat
4020 lok_doc_view_twip_to_pixel (LOKDocView
* pDocView
, float fInput
)
4022 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
4023 return twipToPixel(fInput
, priv
->m_fZoom
);
4026 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */