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>
19 #include <boost/property_tree/json_parser.hpp>
21 #include <com/sun/star/awt/Key.hpp>
22 #include <LibreOfficeKit/LibreOfficeKit.h>
23 #include <LibreOfficeKit/LibreOfficeKitInit.h>
24 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
25 #include <LibreOfficeKit/LibreOfficeKitGtk.h>
26 #include <vcl/event.hxx>
28 #include "tilebuffer.hxx"
30 #if !GLIB_CHECK_VERSION(2,32,0)
31 #define G_SOURCE_REMOVE FALSE
32 #define G_SOURCE_CONTINUE TRUE
34 #if !GLIB_CHECK_VERSION(2,40,0)
35 #define g_info(...) g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, __VA_ARGS__)
38 // Cursor bitmaps from the installation set.
39 #define CURSOR_HANDLE_DIR "/../share/libreofficekit/"
40 // Number of handles around a graphic selection.
41 #define GRAPHIC_HANDLE_COUNT 8
42 // Maximum Zoom allowed
44 // Minimum Zoom allowed
45 #define MIN_ZOOM 0.25f
47 /// This is expected to be locked during setView(), doSomethingElse() LOK calls.
48 static std::mutex g_aLOKMutex
;
52 /// Same as a GdkRectangle, but also tracks in which part the rectangle is.
56 GdkRectangle m_aRectangle
;
58 ViewRectangle(int nPart
= 0, const GdkRectangle
& rRectangle
= GdkRectangle())
60 m_aRectangle(rRectangle
)
65 /// Same as a list of GdkRectangles, but also tracks in which part the rectangle is.
69 std::vector
<GdkRectangle
> m_aRectangles
;
71 ViewRectangles(int nPart
= 0, std::vector
<GdkRectangle
>&& rRectangles
= std::vector
<GdkRectangle
>())
73 m_aRectangles(std::move(rRectangles
))
78 /// Private struct used by this GObject type
79 struct LOKDocViewPrivateImpl
81 std::string m_aLOPath
;
82 std::string m_aUserProfileURL
;
83 std::string m_aDocPath
;
84 std::string m_aRenderingArguments
;
85 gdouble m_nLoadProgress
;
87 bool m_bInit
; // initializeForRendering() has been called
91 LibreOfficeKit
* m_pOffice
;
92 LibreOfficeKitDocument
* m_pDocument
;
94 std::unique_ptr
<TileBuffer
> m_pTileBuffer
;
95 GThreadPool
* lokThreadPool
;
98 glong m_nDocumentWidthTwips
;
99 glong m_nDocumentHeightTwips
;
100 /// View or edit mode.
103 guint64 m_nLOKFeatures
;
104 /// Number of parts in currently loaded document
106 /// Position and size of the visible cursor.
107 GdkRectangle m_aVisibleCursor
;
108 /// Position and size of the view cursors. The current view can only see
109 /// them, can't modify them. Key is the view id.
110 std::map
<int, ViewRectangle
> m_aViewCursors
;
111 /// Cursor overlay is visible or hidden (for blinking).
112 bool m_bCursorOverlayVisible
;
113 /// Cursor is visible or hidden (e.g. for graphic selection).
114 bool m_bCursorVisible
;
115 /// Visibility of view selections. The current view can only see / them,
116 /// can't modify them. Key is the view id.
117 std::map
<int, bool> m_aViewCursorVisibilities
;
118 /// Time of the last button press.
119 guint32 m_nLastButtonPressTime
;
120 /// Time of the last button release.
121 guint32 m_nLastButtonReleaseTime
;
122 /// Last pressed button (left, right, middle)
123 guint32 m_nLastButtonPressed
;
124 /// Key modifier (ctrl, atl, shift)
125 guint32 m_nKeyModifier
;
126 /// Rectangles of the current text selection.
127 std::vector
<GdkRectangle
> m_aTextSelectionRectangles
;
128 /// Rectangles of the current content control.
129 std::vector
<GdkRectangle
> m_aContentControlRectangles
;
130 /// Alias/title of the current content control.
131 std::string m_aContentControlAlias
;
132 /// Rectangles of view selections. The current view can only see
133 /// them, can't modify them. Key is the view id.
134 std::map
<int, ViewRectangles
> m_aTextViewSelectionRectangles
;
135 /// Position and size of the selection start (as if there would be a cursor caret there).
136 GdkRectangle m_aTextSelectionStart
;
137 /// Position and size of the selection end.
138 GdkRectangle m_aTextSelectionEnd
;
139 GdkRectangle m_aGraphicSelection
;
140 /// Position and size of the graphic view selections. The current view can only
141 /// see them, can't modify them. Key is the view id.
142 std::map
<int, ViewRectangle
> m_aGraphicViewSelections
;
143 GdkRectangle m_aCellCursor
;
144 /// Position and size of the cell view cursors. The current view can only
145 /// see them, can't modify them. Key is the view id.
146 std::map
<int, ViewRectangle
> m_aCellViewCursors
;
147 bool m_bInDragGraphicSelection
;
148 /// Position, size and color of the reference marks. The current view can only
149 /// see them, can't modify them. Key is the view id.
150 std::vector
<std::pair
<ViewRectangle
, sal_uInt32
>> m_aReferenceMarks
;
152 /// @name Start/middle/end handle.
154 /// Bitmap of the text selection start handle.
155 cairo_surface_t
* m_pHandleStart
;
156 /// Rectangle of the text selection start handle, to know if the user clicked on it or not
157 GdkRectangle m_aHandleStartRect
;
158 /// If we are in the middle of a drag of the text selection end handle.
159 bool m_bInDragStartHandle
;
160 /// Bitmap of the text selection middle handle.
161 cairo_surface_t
* m_pHandleMiddle
;
162 /// Rectangle of the text selection middle handle, to know if the user clicked on it or not
163 GdkRectangle m_aHandleMiddleRect
;
164 /// If we are in the middle of a drag of the text selection middle handle.
165 bool m_bInDragMiddleHandle
;
166 /// Bitmap of the text selection end handle.
167 cairo_surface_t
* m_pHandleEnd
;
168 /// Rectangle of the text selection end handle, to know if the user clicked on it or not
169 GdkRectangle m_aHandleEndRect
;
170 /// If we are in the middle of a drag of the text selection end handle.
171 bool m_bInDragEndHandle
;
174 /// @name Graphic handles.
176 /// Rectangle of a graphic selection handle, to know if the user clicked on it or not.
177 GdkRectangle m_aGraphicHandleRects
[8];
178 /// If we are in the middle of a drag of a graphic selection handle.
179 bool m_bInDragGraphicHandles
[8];
182 /// View ID, returned by createView() or 0 by default.
185 /// Cached part ID, returned by getPart().
188 /// Cached document type, returned by getDocumentType().
189 LibreOfficeKitDocumentType m_eDocumentType
;
191 /// Contains a freshly set zoom level: logic size of a tile.
192 /// It gets reset back to 0 when LOK was informed about this zoom change.
193 int m_nTileSizeTwips
;
195 GdkRectangle m_aVisibleArea
;
196 bool m_bVisibleAreaSet
;
198 /// Event source ID for handleTimeout() of this widget.
201 /// Rectangles of view locks. The current view can only see
202 /// them, can't modify them. Key is the view id.
203 std::map
<int, ViewRectangle
> m_aViewLockRectangles
;
205 LOKDocViewPrivateImpl()
206 : m_nLoadProgress(0),
213 m_pDocument(nullptr),
214 lokThreadPool(nullptr),
216 m_nDocumentWidthTwips(0),
217 m_nDocumentHeightTwips(0),
221 m_aVisibleCursor({0, 0, 0, 0}),
222 m_bCursorOverlayVisible(false),
223 m_bCursorVisible(true),
224 m_nLastButtonPressTime(0),
225 m_nLastButtonReleaseTime(0),
226 m_nLastButtonPressed(0),
228 m_aTextSelectionStart({0, 0, 0, 0}),
229 m_aTextSelectionEnd({0, 0, 0, 0}),
230 m_aGraphicSelection({0, 0, 0, 0}),
231 m_aCellCursor({0, 0, 0, 0}),
232 m_bInDragGraphicSelection(false),
233 m_pHandleStart(nullptr),
234 m_aHandleStartRect({0, 0, 0, 0}),
235 m_bInDragStartHandle(false),
236 m_pHandleMiddle(nullptr),
237 m_aHandleMiddleRect({0, 0, 0, 0}),
238 m_bInDragMiddleHandle(false),
239 m_pHandleEnd(nullptr),
240 m_aHandleEndRect({0, 0, 0, 0}),
241 m_bInDragEndHandle(false),
244 m_eDocumentType(LOK_DOCTYPE_OTHER
),
246 m_aVisibleArea({0, 0, 0, 0}),
247 m_bVisibleAreaSet(false),
250 memset(&m_aGraphicHandleRects
, 0, sizeof(m_aGraphicHandleRects
));
251 memset(&m_bInDragGraphicHandles
, 0, sizeof(m_bInDragGraphicHandles
));
254 ~LOKDocViewPrivateImpl()
257 g_source_remove(m_nTimeoutId
);
261 // Must be run with g_aLOKMutex locked
262 void setDocumentView(LibreOfficeKitDocument
* pDoc
, int viewId
)
265 std::stringstream ss
;
266 ss
<< "lok::Document::setView(" << viewId
<< ")";
267 g_info("%s", ss
.str().c_str());
268 pDoc
->pClass
->setView(pDoc
, viewId
);
272 /// Wrapper around LOKDocViewPrivateImpl, managed by malloc/memset/free.
273 struct _LOKDocViewPrivate
275 LOKDocViewPrivateImpl
* m_pImpl
;
277 LOKDocViewPrivateImpl
* operator->()
315 PROP_USER_PROFILE_URL
,
328 PROP_DOC_PASSWORD_TO_MODIFY
,
329 PROP_TILED_ANNOTATIONS
,
334 static guint doc_view_signals
[LAST_SIGNAL
] = { 0 };
335 static GParamSpec
*properties
[PROP_LAST
] = { nullptr };
337 static void lok_doc_view_initable_iface_init (GInitableIface
*iface
);
338 static void callbackWorker (int nType
, const char* pPayload
, void* pData
);
339 static void updateClientZoom (LOKDocView
*pDocView
);
342 #pragma GCC diagnostic push
343 #pragma GCC diagnostic ignored "-Wunused-function"
344 #if defined __clang__
345 #if __has_warning("-Wdeprecated-volatile")
346 #pragma clang diagnostic ignored "-Wdeprecated-volatile"
350 G_DEFINE_TYPE_WITH_CODE (LOKDocView
, lok_doc_view
, GTK_TYPE_DRAWING_AREA
,
351 G_ADD_PRIVATE (LOKDocView
)
352 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE
, lok_doc_view_initable_iface_init
));
354 #pragma GCC diagnostic pop
357 static LOKDocViewPrivate
& getPrivate(LOKDocView
* pDocView
)
359 LOKDocViewPrivate
* priv
= static_cast<LOKDocViewPrivate
*>(lok_doc_view_get_instance_private(pDocView
));
365 /// Helper struct used to pass the data from soffice thread -> main thread.
369 std::string m_aPayload
;
370 LOKDocView
* m_pDocView
;
372 CallbackData(int nType
, std::string aPayload
, LOKDocView
* pDocView
)
374 m_aPayload(std::move(aPayload
)),
375 m_pDocView(pDocView
) {}
381 LOKPostCommand (LOKDocView
* pDocView
,
382 const gchar
* pCommand
,
383 const gchar
* pArguments
,
384 bool bNotifyWhenFinished
)
386 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
387 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
388 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_COMMAND
);
389 GError
* error
= nullptr;
390 pLOEvent
->m_pCommand
= g_strdup(pCommand
);
391 pLOEvent
->m_pArguments
= g_strdup(pArguments
);
392 pLOEvent
->m_bNotifyWhenFinished
= bNotifyWhenFinished
;
394 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
395 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
396 if (error
!= nullptr)
398 g_warning("Unable to call LOK_POST_COMMAND: %s", error
->message
);
399 g_clear_error(&error
);
401 g_object_unref(task
);
405 doSearch(LOKDocView
* pDocView
, const char* pText
, bool bBackwards
, bool highlightAll
)
407 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
408 if (!priv
->m_pDocument
)
411 boost::property_tree::ptree aTree
;
412 GtkWidget
* drawingWidget
= GTK_WIDGET(pDocView
);
413 GdkWindow
* drawingWindow
= gtk_widget_get_window(drawingWidget
);
416 std::shared_ptr
<cairo_region_t
> cairoVisRegion( gdk_window_get_visible_region(drawingWindow
),
417 cairo_region_destroy
);
418 cairo_rectangle_int_t cairoVisRect
;
419 cairo_region_get_rectangle(cairoVisRegion
.get(), 0, &cairoVisRect
);
420 int x
= pixelToTwip (cairoVisRect
.x
, priv
->m_fZoom
);
421 int y
= pixelToTwip (cairoVisRect
.y
, priv
->m_fZoom
);
423 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchString/type", '/'), "string");
424 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchString/value", '/'), pText
);
425 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.Backward/type", '/'), "boolean");
426 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.Backward/value", '/'), bBackwards
);
429 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.Command/type", '/'), "unsigned short");
430 // SvxSearchCmd::FIND_ALL
431 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.Command/value", '/'), "1");
434 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointX/type", '/'), "long");
435 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointX/value", '/'), x
);
436 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointY/type", '/'), "long");
437 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointY/value", '/'), y
);
439 std::stringstream aStream
;
440 boost::property_tree::write_json(aStream
, aTree
);
442 LOKPostCommand (pDocView
, ".uno:ExecuteSearch", aStream
.str().c_str(), false);
446 isEmptyRectangle(const GdkRectangle
& rRectangle
)
448 return rRectangle
.x
== 0 && rRectangle
.y
== 0 && rRectangle
.width
== 0 && rRectangle
.height
== 0;
451 /// if handled, returns TRUE else FALSE
453 handleTextSelectionOnButtonPress(GdkRectangle
& aClick
, LOKDocView
* pDocView
) {
454 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
456 if (gdk_rectangle_intersect(&aClick
, &priv
->m_aHandleStartRect
, nullptr))
458 g_info("LOKDocView_Impl::signalButton: start of drag start handle");
459 priv
->m_bInDragStartHandle
= true;
462 else if (gdk_rectangle_intersect(&aClick
, &priv
->m_aHandleMiddleRect
, nullptr))
464 g_info("LOKDocView_Impl::signalButton: start of drag middle handle");
465 priv
->m_bInDragMiddleHandle
= true;
468 else if (gdk_rectangle_intersect(&aClick
, &priv
->m_aHandleEndRect
, nullptr))
470 g_info("LOKDocView_Impl::signalButton: start of drag end handle");
471 priv
->m_bInDragEndHandle
= true;
478 /// if handled, returns TRUE else FALSE
480 handleGraphicSelectionOnButtonPress(GdkRectangle
& aClick
, LOKDocView
* pDocView
) {
481 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
482 GError
* error
= nullptr;
484 for (int i
= 0; i
< GRAPHIC_HANDLE_COUNT
; ++i
)
486 if (gdk_rectangle_intersect(&aClick
, &priv
->m_aGraphicHandleRects
[i
], nullptr))
488 g_info("LOKDocView_Impl::signalButton: start of drag graphic handle #%d", i
);
489 priv
->m_bInDragGraphicHandles
[i
] = true;
491 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
492 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_GRAPHIC_SELECTION
);
493 pLOEvent
->m_nSetGraphicSelectionType
= LOK_SETGRAPHICSELECTION_START
;
494 pLOEvent
->m_nSetGraphicSelectionX
= pixelToTwip(priv
->m_aGraphicHandleRects
[i
].x
+ priv
->m_aGraphicHandleRects
[i
].width
/ 2, priv
->m_fZoom
);
495 pLOEvent
->m_nSetGraphicSelectionY
= pixelToTwip(priv
->m_aGraphicHandleRects
[i
].y
+ priv
->m_aGraphicHandleRects
[i
].height
/ 2, priv
->m_fZoom
);
496 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
498 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
499 if (error
!= nullptr)
501 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error
->message
);
502 g_clear_error(&error
);
504 g_object_unref(task
);
513 /// if handled, returns TRUE else FALSE
515 handleTextSelectionOnButtonRelease(LOKDocView
* pDocView
) {
516 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
518 if (priv
->m_bInDragStartHandle
)
520 g_info("LOKDocView_Impl::signalButton: end of drag start handle");
521 priv
->m_bInDragStartHandle
= false;
524 else if (priv
->m_bInDragMiddleHandle
)
526 g_info("LOKDocView_Impl::signalButton: end of drag middle handle");
527 priv
->m_bInDragMiddleHandle
= false;
530 else if (priv
->m_bInDragEndHandle
)
532 g_info("LOKDocView_Impl::signalButton: end of drag end handle");
533 priv
->m_bInDragEndHandle
= false;
540 /// if handled, returns TRUE else FALSE
542 handleGraphicSelectionOnButtonRelease(LOKDocView
* pDocView
, GdkEventButton
* pEvent
) {
543 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
544 GError
* error
= nullptr;
546 for (int i
= 0; i
< GRAPHIC_HANDLE_COUNT
; ++i
)
548 if (priv
->m_bInDragGraphicHandles
[i
])
550 g_info("LOKDocView_Impl::signalButton: end of drag graphic handle #%d", i
);
551 priv
->m_bInDragGraphicHandles
[i
] = false;
553 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
554 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_GRAPHIC_SELECTION
);
555 pLOEvent
->m_nSetGraphicSelectionType
= LOK_SETGRAPHICSELECTION_END
;
556 pLOEvent
->m_nSetGraphicSelectionX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
557 pLOEvent
->m_nSetGraphicSelectionY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
558 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
560 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
561 if (error
!= nullptr)
563 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error
->message
);
564 g_clear_error(&error
);
566 g_object_unref(task
);
572 if (!priv
->m_bInDragGraphicSelection
)
575 g_info("LOKDocView_Impl::signalButton: end of drag graphic selection");
576 priv
->m_bInDragGraphicSelection
= false;
578 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
579 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_GRAPHIC_SELECTION
);
580 pLOEvent
->m_nSetGraphicSelectionType
= LOK_SETGRAPHICSELECTION_END
;
581 pLOEvent
->m_nSetGraphicSelectionX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
582 pLOEvent
->m_nSetGraphicSelectionY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
583 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
585 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
586 if (error
!= nullptr)
588 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error
->message
);
589 g_clear_error(&error
);
591 g_object_unref(task
);
597 postKeyEventInThread(gpointer data
)
599 GTask
* task
= G_TASK(data
);
600 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
601 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
602 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
603 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
604 gint nTileSizePixelsScaled
= nTileSizePixels
* nScaleFactor
;
606 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
607 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
608 std::stringstream ss
;
610 if (priv
->m_nTileSizeTwips
)
612 ss
.str(std::string());
613 ss
<< "lok::Document::setClientZoom(" << nTileSizePixelsScaled
<< ", " << nTileSizePixelsScaled
<< ", " << priv
->m_nTileSizeTwips
<< ", " << priv
->m_nTileSizeTwips
<< ")";
614 g_info("%s", ss
.str().c_str());
615 priv
->m_pDocument
->pClass
->setClientZoom(priv
->m_pDocument
,
616 nTileSizePixelsScaled
,
617 nTileSizePixelsScaled
,
618 priv
->m_nTileSizeTwips
,
619 priv
->m_nTileSizeTwips
);
620 priv
->m_nTileSizeTwips
= 0;
622 if (priv
->m_bVisibleAreaSet
)
624 ss
.str(std::string());
625 ss
<< "lok::Document::setClientVisibleArea(" << priv
->m_aVisibleArea
.x
<< ", " << priv
->m_aVisibleArea
.y
<< ", ";
626 ss
<< priv
->m_aVisibleArea
.width
<< ", " << priv
->m_aVisibleArea
.height
<< ")";
627 g_info("%s", ss
.str().c_str());
628 priv
->m_pDocument
->pClass
->setClientVisibleArea(priv
->m_pDocument
,
629 priv
->m_aVisibleArea
.x
,
630 priv
->m_aVisibleArea
.y
,
631 priv
->m_aVisibleArea
.width
,
632 priv
->m_aVisibleArea
.height
);
633 priv
->m_bVisibleAreaSet
= false;
636 ss
.str(std::string());
637 ss
<< "lok::Document::postKeyEvent(" << pLOEvent
->m_nKeyEvent
<< ", " << pLOEvent
->m_nCharCode
<< ", " << pLOEvent
->m_nKeyCode
<< ")";
638 g_info("%s", ss
.str().c_str());
639 priv
->m_pDocument
->pClass
->postKeyEvent(priv
->m_pDocument
,
640 pLOEvent
->m_nKeyEvent
,
641 pLOEvent
->m_nCharCode
,
642 pLOEvent
->m_nKeyCode
);
646 signalKey (GtkWidget
* pWidget
, GdkEventKey
* pEvent
)
648 LOKDocView
* pDocView
= LOK_DOC_VIEW(pWidget
);
649 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
652 GError
* error
= nullptr;
656 g_info("signalKey: not in edit mode, ignore");
660 priv
->m_nKeyModifier
&= KEY_MOD2
;
661 switch (pEvent
->keyval
)
663 case GDK_KEY_BackSpace
:
664 nKeyCode
= com::sun::star::awt::Key::BACKSPACE
;
667 nKeyCode
= com::sun::star::awt::Key::DELETE
;
670 case GDK_KEY_KP_Enter
:
671 nKeyCode
= com::sun::star::awt::Key::RETURN
;
674 nKeyCode
= com::sun::star::awt::Key::ESCAPE
;
677 nKeyCode
= com::sun::star::awt::Key::TAB
;
680 nKeyCode
= com::sun::star::awt::Key::DOWN
;
683 nKeyCode
= com::sun::star::awt::Key::UP
;
686 nKeyCode
= com::sun::star::awt::Key::LEFT
;
689 nKeyCode
= com::sun::star::awt::Key::RIGHT
;
691 case GDK_KEY_Page_Down
:
692 nKeyCode
= com::sun::star::awt::Key::PAGEDOWN
;
694 case GDK_KEY_Page_Up
:
695 nKeyCode
= com::sun::star::awt::Key::PAGEUP
;
698 nKeyCode
= com::sun::star::awt::Key::INSERT
;
700 case GDK_KEY_Shift_L
:
701 case GDK_KEY_Shift_R
:
702 if (pEvent
->type
== GDK_KEY_PRESS
)
703 priv
->m_nKeyModifier
|= KEY_SHIFT
;
705 case GDK_KEY_Control_L
:
706 case GDK_KEY_Control_R
:
707 if (pEvent
->type
== GDK_KEY_PRESS
)
708 priv
->m_nKeyModifier
|= KEY_MOD1
;
712 if (pEvent
->type
== GDK_KEY_PRESS
)
713 priv
->m_nKeyModifier
|= KEY_MOD2
;
715 priv
->m_nKeyModifier
&= ~KEY_MOD2
;
718 if (pEvent
->keyval
>= GDK_KEY_F1
&& pEvent
->keyval
<= GDK_KEY_F26
)
719 nKeyCode
= com::sun::star::awt::Key::F1
+ (pEvent
->keyval
- GDK_KEY_F1
);
721 nCharCode
= gdk_keyval_to_unicode(pEvent
->keyval
);
724 // rsc is not public API, but should be good enough for debugging purposes.
725 // If this is needed for real, then probably a new param of type
726 // css::awt::KeyModifier is needed in postKeyEvent().
727 if (pEvent
->state
& GDK_SHIFT_MASK
)
728 nKeyCode
|= KEY_SHIFT
;
730 if (pEvent
->state
& GDK_CONTROL_MASK
)
731 nKeyCode
|= KEY_MOD1
;
733 if (priv
->m_nKeyModifier
& KEY_MOD2
)
734 nKeyCode
|= KEY_MOD2
;
736 if (nKeyCode
& (KEY_SHIFT
| KEY_MOD1
| KEY_MOD2
)) {
737 if (pEvent
->keyval
>= GDK_KEY_a
&& pEvent
->keyval
<= GDK_KEY_z
)
739 nKeyCode
|= 512 + (pEvent
->keyval
- GDK_KEY_a
);
741 else if (pEvent
->keyval
>= GDK_KEY_A
&& pEvent
->keyval
<= GDK_KEY_Z
) {
742 nKeyCode
|= 512 + (pEvent
->keyval
- GDK_KEY_A
);
744 else if (pEvent
->keyval
>= GDK_KEY_0
&& pEvent
->keyval
<= GDK_KEY_9
) {
745 nKeyCode
|= 256 + (pEvent
->keyval
- GDK_KEY_0
);
749 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
750 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_KEY
);
751 pLOEvent
->m_nKeyEvent
= pEvent
->type
== GDK_KEY_RELEASE
? LOK_KEYEVENT_KEYUP
: LOK_KEYEVENT_KEYINPUT
;
752 pLOEvent
->m_nCharCode
= nCharCode
;
753 pLOEvent
->m_nKeyCode
= nKeyCode
;
754 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
755 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
756 if (error
!= nullptr)
758 g_warning("Unable to call LOK_POST_KEY: %s", error
->message
);
759 g_clear_error(&error
);
761 g_object_unref(task
);
767 handleTimeout (gpointer pData
)
769 LOKDocView
* pDocView
= LOK_DOC_VIEW (pData
);
770 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
774 if (priv
->m_bCursorOverlayVisible
)
775 priv
->m_bCursorOverlayVisible
= false;
777 priv
->m_bCursorOverlayVisible
= true;
778 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
781 return G_SOURCE_CONTINUE
;
785 commandChanged(LOKDocView
* pDocView
, const std::string
& rString
)
787 g_signal_emit(pDocView
, doc_view_signals
[COMMAND_CHANGED
], 0, rString
.c_str());
791 searchNotFound(LOKDocView
* pDocView
, const std::string
& rString
)
793 g_signal_emit(pDocView
, doc_view_signals
[SEARCH_NOT_FOUND
], 0, rString
.c_str());
796 static void searchResultCount(LOKDocView
* pDocView
, const std::string
& rString
)
798 g_signal_emit(pDocView
, doc_view_signals
[SEARCH_RESULT_COUNT
], 0, rString
.c_str());
801 static void commandResult(LOKDocView
* pDocView
, const std::string
& rString
)
803 g_signal_emit(pDocView
, doc_view_signals
[COMMAND_RESULT
], 0, rString
.c_str());
806 static void addressChanged(LOKDocView
* pDocView
, const std::string
& rString
)
808 g_signal_emit(pDocView
, doc_view_signals
[ADDRESS_CHANGED
], 0, rString
.c_str());
811 static void formulaChanged(LOKDocView
* pDocView
, const std::string
& rString
)
813 g_signal_emit(pDocView
, doc_view_signals
[FORMULA_CHANGED
], 0, rString
.c_str());
816 static void reportError(LOKDocView
* /*pDocView*/, const std::string
& rString
)
818 GtkWidget
*dialog
= gtk_message_dialog_new(nullptr,
819 GTK_DIALOG_DESTROY_WITH_PARENT
,
824 gtk_dialog_run(GTK_DIALOG(dialog
));
825 gtk_widget_destroy(dialog
);
829 setPart(LOKDocView
* pDocView
, const std::string
& rString
)
831 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
832 priv
->m_nPartId
= std::stoi(rString
);
833 g_signal_emit(pDocView
, doc_view_signals
[PART_CHANGED
], 0, priv
->m_nPartId
);
837 hyperlinkClicked(LOKDocView
* pDocView
, const std::string
& rString
)
839 g_signal_emit(pDocView
, doc_view_signals
[HYPERLINK_CLICKED
], 0, rString
.c_str());
842 /// Trigger a redraw, invoked on the main thread by other functions running in a thread.
843 static gboolean
queueDraw(gpointer pData
)
845 GtkWidget
* pWidget
= static_cast<GtkWidget
*>(pData
);
847 gtk_widget_queue_draw(pWidget
);
849 return G_SOURCE_REMOVE
;
852 /// Looks up the author string from initializeForRendering()'s rendering arguments.
853 static std::string
getAuthorRenderingArgument(LOKDocViewPrivate
& priv
)
855 std::stringstream aStream
;
856 aStream
<< priv
->m_aRenderingArguments
;
857 boost::property_tree::ptree aTree
;
858 boost::property_tree::read_json(aStream
, aTree
);
860 for (const auto& rPair
: aTree
)
862 if (rPair
.first
== ".uno:Author")
864 aRet
= rPair
.second
.get
<std::string
>("value");
871 /// Author string <-> View ID map
872 static std::map
<std::string
, int> g_aAuthorViews
;
874 static void refreshSize(LOKDocView
* pDocView
)
876 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
878 priv
->m_pDocument
->pClass
->getDocumentSize(priv
->m_pDocument
, &priv
->m_nDocumentWidthTwips
, &priv
->m_nDocumentHeightTwips
);
879 float zoom
= priv
->m_fZoom
;
880 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
881 gint nTileSizePixelsScaled
= nTileSizePixels
* nScaleFactor
;
882 long nDocumentWidthTwips
= priv
->m_nDocumentWidthTwips
;
883 long nDocumentHeightTwips
= priv
->m_nDocumentHeightTwips
;
884 long nDocumentWidthPixels
= twipToPixel(nDocumentWidthTwips
, zoom
);
885 long nDocumentHeightPixels
= twipToPixel(nDocumentHeightTwips
, zoom
);
887 // Total number of columns in this document.
888 guint nColumns
= ceil(static_cast<double>(nDocumentWidthPixels
) / nTileSizePixelsScaled
);
889 priv
->m_pTileBuffer
= std::make_unique
<TileBuffer
>(nColumns
, nScaleFactor
);
890 gtk_widget_set_size_request(GTK_WIDGET(pDocView
),
891 nDocumentWidthPixels
,
892 nDocumentHeightPixels
);
895 /// Set up LOKDocView after the document is loaded, invoked on the main thread by openDocumentInThread() running in a thread.
896 static gboolean
postDocumentLoad(gpointer pData
)
898 LOKDocView
* pLOKDocView
= static_cast<LOKDocView
*>(pData
);
899 LOKDocViewPrivate
& priv
= getPrivate(pLOKDocView
);
901 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
902 priv
->m_pDocument
->pClass
->initializeForRendering(priv
->m_pDocument
, priv
->m_aRenderingArguments
.c_str());
903 // This returns the view id of the "current" view, but sadly if you load multiple documents that
904 // is apparently not a view showing the most recently loaded document. Not much we can do here,
905 // though. If that is fixed, this comment becomes incorrect.
906 priv
->m_nViewId
= priv
->m_pDocument
->pClass
->getView(priv
->m_pDocument
);
907 g_aAuthorViews
[getAuthorRenderingArgument(priv
)] = priv
->m_nViewId
;
908 priv
->m_pDocument
->pClass
->registerCallback(priv
->m_pDocument
, callbackWorker
, pLOKDocView
);
909 priv
->m_nParts
= priv
->m_pDocument
->pClass
->getParts(priv
->m_pDocument
);
911 priv
->m_nTimeoutId
= g_timeout_add(600, handleTimeout
, pLOKDocView
);
913 refreshSize(pLOKDocView
);
915 gtk_widget_set_can_focus(GTK_WIDGET(pLOKDocView
), true);
916 gtk_widget_grab_focus(GTK_WIDGET(pLOKDocView
));
917 lok_doc_view_set_zoom(pLOKDocView
, 1.0);
919 // we are completely loaded
920 priv
->m_bInit
= true;
921 g_object_notify_by_pspec(G_OBJECT(pLOKDocView
), properties
[PROP_IS_INITIALIZED
]);
923 return G_SOURCE_REMOVE
;
926 /// Implementation of the global callback handler, invoked by globalCallback();
928 globalCallback (gpointer pData
)
930 CallbackData
* pCallback
= static_cast<CallbackData
*>(pData
);
931 LOKDocViewPrivate
& priv
= getPrivate(pCallback
->m_pDocView
);
932 bool bModify
= false;
934 switch (pCallback
->m_nType
)
936 case LOK_CALLBACK_STATUS_INDICATOR_START
:
938 priv
->m_nLoadProgress
= 0.0;
939 g_signal_emit (pCallback
->m_pDocView
, doc_view_signals
[LOAD_CHANGED
], 0, 0.0);
942 case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE
:
944 priv
->m_nLoadProgress
= static_cast<gdouble
>(std::stoi(pCallback
->m_aPayload
)/100.0);
945 g_signal_emit (pCallback
->m_pDocView
, doc_view_signals
[LOAD_CHANGED
], 0, priv
->m_nLoadProgress
);
948 case LOK_CALLBACK_STATUS_INDICATOR_FINISH
:
950 priv
->m_nLoadProgress
= 1.0;
951 g_signal_emit (pCallback
->m_pDocView
, doc_view_signals
[LOAD_CHANGED
], 0, 1.0);
954 case LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY
:
957 case LOK_CALLBACK_DOCUMENT_PASSWORD
:
959 char const*const pURL(pCallback
->m_aPayload
.c_str());
960 g_signal_emit (pCallback
->m_pDocView
, doc_view_signals
[PASSWORD_REQUIRED
], 0, pURL
, bModify
);
963 case LOK_CALLBACK_ERROR
:
965 reportError(pCallback
->m_pDocView
, pCallback
->m_aPayload
);
968 case LOK_CALLBACK_SIGNATURE_STATUS
:
979 return G_SOURCE_REMOVE
;
983 globalCallbackWorker(int nType
, const char* pPayload
, void* pData
)
985 LOKDocView
* pDocView
= LOK_DOC_VIEW (pData
);
987 CallbackData
* pCallback
= new CallbackData(nType
, pPayload
? pPayload
: "(nil)", pDocView
);
988 g_info("LOKDocView_Impl::globalCallbackWorkerImpl: %s, '%s'", lokCallbackTypeToString(nType
), pPayload
);
989 gdk_threads_add_idle(globalCallback
, pCallback
);
993 payloadToRectangle (LOKDocView
* pDocView
, const char* pPayload
)
995 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
997 // x, y, width, height, part number.
998 gchar
** ppCoordinates
= g_strsplit(pPayload
, ", ", 5);
999 gchar
** ppCoordinate
= ppCoordinates
;
1001 aRet
.width
= aRet
.height
= aRet
.x
= aRet
.y
= 0;
1005 g_strfreev(ppCoordinates
);
1008 aRet
.x
= atoi(*ppCoordinate
);
1014 g_strfreev(ppCoordinates
);
1017 aRet
.y
= atoi(*ppCoordinate
);
1023 g_strfreev(ppCoordinates
);
1026 long l
= atol(*ppCoordinate
);
1027 if (l
> std::numeric_limits
<int>::max())
1028 aRet
.width
= std::numeric_limits
<int>::max();
1031 if (aRet
.x
+ aRet
.width
> priv
->m_nDocumentWidthTwips
)
1032 aRet
.width
= priv
->m_nDocumentWidthTwips
- aRet
.x
;
1036 g_strfreev(ppCoordinates
);
1039 l
= atol(*ppCoordinate
);
1040 if (l
> std::numeric_limits
<int>::max())
1041 aRet
.height
= std::numeric_limits
<int>::max();
1044 if (aRet
.y
+ aRet
.height
> priv
->m_nDocumentHeightTwips
)
1045 aRet
.height
= priv
->m_nDocumentHeightTwips
- aRet
.y
;
1047 g_strfreev(ppCoordinates
);
1051 static std::vector
<GdkRectangle
>
1052 payloadToRectangles(LOKDocView
* pDocView
, const char* pPayload
)
1054 std::vector
<GdkRectangle
> aRet
;
1056 if (g_strcmp0(pPayload
, "EMPTY") == 0)
1059 gchar
** ppRectangles
= g_strsplit(pPayload
, "; ", 0);
1060 for (gchar
** ppRectangle
= ppRectangles
; *ppRectangle
; ++ppRectangle
)
1061 aRet
.push_back(payloadToRectangle(pDocView
, *ppRectangle
));
1062 g_strfreev(ppRectangles
);
1069 setTilesInvalid (LOKDocView
* pDocView
, const GdkRectangle
& rRectangle
)
1071 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1072 GdkRectangle aRectanglePixels
;
1073 GdkPoint aStart
, aEnd
;
1074 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
1075 gint nTileSizePixelsScaled
= nTileSizePixels
* nScaleFactor
;
1077 aRectanglePixels
.x
= twipToPixel(rRectangle
.x
, priv
->m_fZoom
) * nScaleFactor
;
1078 aRectanglePixels
.y
= twipToPixel(rRectangle
.y
, priv
->m_fZoom
) * nScaleFactor
;
1079 aRectanglePixels
.width
= twipToPixel(rRectangle
.width
, priv
->m_fZoom
) * nScaleFactor
;
1080 aRectanglePixels
.height
= twipToPixel(rRectangle
.height
, priv
->m_fZoom
) * nScaleFactor
;
1082 aStart
.x
= aRectanglePixels
.y
/ nTileSizePixelsScaled
;
1083 aStart
.y
= aRectanglePixels
.x
/ nTileSizePixelsScaled
;
1084 aEnd
.x
= (aRectanglePixels
.y
+ aRectanglePixels
.height
+ nTileSizePixelsScaled
) / nTileSizePixelsScaled
;
1085 aEnd
.y
= (aRectanglePixels
.x
+ aRectanglePixels
.width
+ nTileSizePixelsScaled
) / nTileSizePixelsScaled
;
1086 for (int i
= aStart
.x
; i
< aEnd
.x
; i
++)
1088 for (int j
= aStart
.y
; j
< aEnd
.y
; j
++)
1090 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
1091 priv
->m_pTileBuffer
->setInvalid(i
, j
, priv
->m_fZoom
, task
, priv
->lokThreadPool
);
1092 g_object_unref(task
);
1098 callback (gpointer pData
)
1100 CallbackData
* pCallback
= static_cast<CallbackData
*>(pData
);
1101 LOKDocView
* pDocView
= LOK_DOC_VIEW (pCallback
->m_pDocView
);
1102 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1104 //callback registered before the widget was destroyed.
1105 //Use existence of lokThreadPool as flag it was torn down
1106 if (!priv
->lokThreadPool
)
1109 return G_SOURCE_REMOVE
;
1112 switch (static_cast<LibreOfficeKitCallbackType
>(pCallback
->m_nType
))
1114 case LOK_CALLBACK_INVALIDATE_TILES
:
1116 if (pCallback
->m_aPayload
.compare(0, 5, "EMPTY") != 0) // payload doesn't start with "EMPTY"
1118 GdkRectangle aRectangle
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1119 setTilesInvalid(pDocView
, aRectangle
);
1122 priv
->m_pTileBuffer
->resetAllTiles();
1124 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1127 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
:
1130 std::stringstream
aStream(pCallback
->m_aPayload
);
1131 boost::property_tree::ptree aTree
;
1132 boost::property_tree::read_json(aStream
, aTree
);
1133 const std::string
& rRectangle
= aTree
.get
<std::string
>("rectangle");
1134 int nViewId
= aTree
.get
<int>("viewId");
1136 priv
->m_aVisibleCursor
= payloadToRectangle(pDocView
, rRectangle
.c_str());
1137 priv
->m_bCursorOverlayVisible
= true;
1138 if(nViewId
== priv
->m_nViewId
)
1140 g_signal_emit(pDocView
, doc_view_signals
[CURSOR_CHANGED
], 0,
1141 priv
->m_aVisibleCursor
.x
,
1142 priv
->m_aVisibleCursor
.y
,
1143 priv
->m_aVisibleCursor
.width
,
1144 priv
->m_aVisibleCursor
.height
);
1146 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1149 case LOK_CALLBACK_TEXT_SELECTION
:
1151 priv
->m_aTextSelectionRectangles
= payloadToRectangles(pDocView
, pCallback
->m_aPayload
.c_str());
1152 bool bIsTextSelected
= !priv
->m_aTextSelectionRectangles
.empty();
1153 // In case the selection is empty, then we get no LOK_CALLBACK_TEXT_SELECTION_START/END events.
1154 if (!bIsTextSelected
)
1156 memset(&priv
->m_aTextSelectionStart
, 0, sizeof(priv
->m_aTextSelectionStart
));
1157 memset(&priv
->m_aHandleStartRect
, 0, sizeof(priv
->m_aHandleStartRect
));
1158 memset(&priv
->m_aTextSelectionEnd
, 0, sizeof(priv
->m_aTextSelectionEnd
));
1159 memset(&priv
->m_aHandleEndRect
, 0, sizeof(priv
->m_aHandleEndRect
));
1162 memset(&priv
->m_aHandleMiddleRect
, 0, sizeof(priv
->m_aHandleMiddleRect
));
1164 g_signal_emit(pDocView
, doc_view_signals
[TEXT_SELECTION
], 0, bIsTextSelected
);
1165 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1168 case LOK_CALLBACK_TEXT_SELECTION_START
:
1170 priv
->m_aTextSelectionStart
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1173 case LOK_CALLBACK_TEXT_SELECTION_END
:
1175 priv
->m_aTextSelectionEnd
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1178 case LOK_CALLBACK_CURSOR_VISIBLE
:
1180 priv
->m_bCursorVisible
= pCallback
->m_aPayload
== "true";
1183 case LOK_CALLBACK_MOUSE_POINTER
:
1185 // We do not want the cursor to get changed in view-only mode
1188 // The gtk docs claim that most css cursors should be supported, however
1189 // on my system at least this is not true and many cursors are unsupported.
1190 // In this case pCursor = null, which results in the default cursor
1192 GdkCursor
* pCursor
= gdk_cursor_new_from_name(gtk_widget_get_display(GTK_WIDGET(pDocView
)),
1193 pCallback
->m_aPayload
.c_str());
1194 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(pDocView
)), pCursor
);
1198 case LOK_CALLBACK_GRAPHIC_SELECTION
:
1200 if (pCallback
->m_aPayload
!= "EMPTY")
1201 priv
->m_aGraphicSelection
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1203 memset(&priv
->m_aGraphicSelection
, 0, sizeof(priv
->m_aGraphicSelection
));
1204 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1207 case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION
:
1209 std::stringstream
aStream(pCallback
->m_aPayload
);
1210 boost::property_tree::ptree aTree
;
1211 boost::property_tree::read_json(aStream
, aTree
);
1212 int nViewId
= aTree
.get
<int>("viewId");
1213 int nPart
= aTree
.get
<int>("part");
1214 const std::string
& rRectangle
= aTree
.get
<std::string
>("selection");
1215 if (rRectangle
!= "EMPTY")
1216 priv
->m_aGraphicViewSelections
[nViewId
] = ViewRectangle(nPart
, payloadToRectangle(pDocView
, rRectangle
.c_str()));
1219 auto it
= priv
->m_aGraphicViewSelections
.find(nViewId
);
1220 if (it
!= priv
->m_aGraphicViewSelections
.end())
1221 priv
->m_aGraphicViewSelections
.erase(it
);
1223 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1227 case LOK_CALLBACK_CELL_CURSOR
:
1229 if (pCallback
->m_aPayload
!= "EMPTY")
1230 priv
->m_aCellCursor
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1232 memset(&priv
->m_aCellCursor
, 0, sizeof(priv
->m_aCellCursor
));
1233 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1236 case LOK_CALLBACK_HYPERLINK_CLICKED
:
1238 hyperlinkClicked(pDocView
, pCallback
->m_aPayload
);
1241 case LOK_CALLBACK_STATE_CHANGED
:
1243 commandChanged(pDocView
, pCallback
->m_aPayload
);
1246 case LOK_CALLBACK_SEARCH_NOT_FOUND
:
1248 searchNotFound(pDocView
, pCallback
->m_aPayload
);
1251 case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED
:
1253 refreshSize(pDocView
);
1254 g_signal_emit(pDocView
, doc_view_signals
[SIZE_CHANGED
], 0, nullptr);
1257 case LOK_CALLBACK_SET_PART
:
1259 setPart(pDocView
, pCallback
->m_aPayload
);
1262 case LOK_CALLBACK_SEARCH_RESULT_SELECTION
:
1264 boost::property_tree::ptree aTree
;
1265 std::stringstream
aStream(pCallback
->m_aPayload
);
1266 boost::property_tree::read_json(aStream
, aTree
);
1267 int nCount
= aTree
.get_child("searchResultSelection").size();
1268 searchResultCount(pDocView
, std::to_string(nCount
));
1271 case LOK_CALLBACK_UNO_COMMAND_RESULT
:
1273 commandResult(pDocView
, pCallback
->m_aPayload
);
1276 case LOK_CALLBACK_CELL_ADDRESS
:
1278 addressChanged(pDocView
, pCallback
->m_aPayload
);
1281 case LOK_CALLBACK_CELL_FORMULA
:
1283 formulaChanged(pDocView
, pCallback
->m_aPayload
);
1286 case LOK_CALLBACK_ERROR
:
1288 reportError(pDocView
, pCallback
->m_aPayload
);
1291 case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR
:
1293 std::stringstream
aStream(pCallback
->m_aPayload
);
1294 boost::property_tree::ptree aTree
;
1295 boost::property_tree::read_json(aStream
, aTree
);
1296 int nViewId
= aTree
.get
<int>("viewId");
1297 int nPart
= aTree
.get
<int>("part");
1298 const std::string
& rRectangle
= aTree
.get
<std::string
>("rectangle");
1299 priv
->m_aViewCursors
[nViewId
] = ViewRectangle(nPart
, payloadToRectangle(pDocView
, rRectangle
.c_str()));
1300 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1303 case LOK_CALLBACK_TEXT_VIEW_SELECTION
:
1305 std::stringstream
aStream(pCallback
->m_aPayload
);
1306 boost::property_tree::ptree aTree
;
1307 boost::property_tree::read_json(aStream
, aTree
);
1308 int nViewId
= aTree
.get
<int>("viewId");
1309 int nPart
= aTree
.get
<int>("part");
1310 const std::string
& rSelection
= aTree
.get
<std::string
>("selection");
1311 priv
->m_aTextViewSelectionRectangles
[nViewId
] = ViewRectangles(nPart
, payloadToRectangles(pDocView
, rSelection
.c_str()));
1312 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1315 case LOK_CALLBACK_VIEW_CURSOR_VISIBLE
:
1317 std::stringstream
aStream(pCallback
->m_aPayload
);
1318 boost::property_tree::ptree aTree
;
1319 boost::property_tree::read_json(aStream
, aTree
);
1320 int nViewId
= aTree
.get
<int>("viewId");
1321 const std::string
& rVisible
= aTree
.get
<std::string
>("visible");
1322 priv
->m_aViewCursorVisibilities
[nViewId
] = rVisible
== "true";
1323 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1327 case LOK_CALLBACK_CELL_VIEW_CURSOR
:
1329 std::stringstream
aStream(pCallback
->m_aPayload
);
1330 boost::property_tree::ptree aTree
;
1331 boost::property_tree::read_json(aStream
, aTree
);
1332 int nViewId
= aTree
.get
<int>("viewId");
1333 int nPart
= aTree
.get
<int>("part");
1334 const std::string
& rRectangle
= aTree
.get
<std::string
>("rectangle");
1335 if (rRectangle
!= "EMPTY")
1336 priv
->m_aCellViewCursors
[nViewId
] = ViewRectangle(nPart
, payloadToRectangle(pDocView
, rRectangle
.c_str()));
1339 auto it
= priv
->m_aCellViewCursors
.find(nViewId
);
1340 if (it
!= priv
->m_aCellViewCursors
.end())
1341 priv
->m_aCellViewCursors
.erase(it
);
1343 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1346 case LOK_CALLBACK_VIEW_LOCK
:
1348 std::stringstream
aStream(pCallback
->m_aPayload
);
1349 boost::property_tree::ptree aTree
;
1350 boost::property_tree::read_json(aStream
, aTree
);
1351 int nViewId
= aTree
.get
<int>("viewId");
1352 int nPart
= aTree
.get
<int>("part");
1353 const std::string
& rRectangle
= aTree
.get
<std::string
>("rectangle");
1354 if (rRectangle
!= "EMPTY")
1355 priv
->m_aViewLockRectangles
[nViewId
] = ViewRectangle(nPart
, payloadToRectangle(pDocView
, rRectangle
.c_str()));
1358 auto it
= priv
->m_aViewLockRectangles
.find(nViewId
);
1359 if (it
!= priv
->m_aViewLockRectangles
.end())
1360 priv
->m_aViewLockRectangles
.erase(it
);
1362 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1365 case LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED
:
1369 case LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED
:
1373 case LOK_CALLBACK_COMMENT
:
1374 g_signal_emit(pCallback
->m_pDocView
, doc_view_signals
[COMMENT
], 0, pCallback
->m_aPayload
.c_str());
1376 case LOK_CALLBACK_RULER_UPDATE
:
1377 g_signal_emit(pCallback
->m_pDocView
, doc_view_signals
[RULER
], 0, pCallback
->m_aPayload
.c_str());
1379 case LOK_CALLBACK_WINDOW
:
1380 g_signal_emit(pCallback
->m_pDocView
, doc_view_signals
[WINDOW
], 0, pCallback
->m_aPayload
.c_str());
1382 case LOK_CALLBACK_INVALIDATE_HEADER
:
1383 g_signal_emit(pCallback
->m_pDocView
, doc_view_signals
[INVALIDATE_HEADER
], 0, pCallback
->m_aPayload
.c_str());
1385 case LOK_CALLBACK_REFERENCE_MARKS
:
1387 std::stringstream
aStream(pCallback
->m_aPayload
);
1388 boost::property_tree::ptree aTree
;
1389 boost::property_tree::read_json(aStream
, aTree
);
1391 priv
->m_aReferenceMarks
.clear();
1393 for(const auto& rMark
: aTree
.get_child("marks"))
1395 sal_uInt32 nColor
= std::stoi(rMark
.second
.get
<std::string
>("color"), nullptr, 16);
1396 std::string sRect
= rMark
.second
.get
<std::string
>("rectangle");
1397 sal_uInt32 nPart
= std::stoi(rMark
.second
.get
<std::string
>("part"));
1399 GdkRectangle aRect
= payloadToRectangle(pDocView
, sRect
.c_str());
1400 priv
->m_aReferenceMarks
.push_back(std::pair
<ViewRectangle
, sal_uInt32
>(ViewRectangle(nPart
, aRect
), nColor
));
1403 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1407 case LOK_CALLBACK_CONTENT_CONTROL
:
1409 std::stringstream
aPayloadStream(pCallback
->m_aPayload
);
1410 boost::property_tree::ptree aTree
;
1411 boost::property_tree::read_json(aPayloadStream
, aTree
);
1412 auto aAction
= aTree
.get
<std::string
>("action");
1413 if (aAction
== "show")
1415 auto aRectangles
= aTree
.get
<std::string
>("rectangles");
1416 priv
->m_aContentControlRectangles
= payloadToRectangles(pDocView
, aRectangles
.c_str());
1418 auto it
= aTree
.find("alias");
1419 if (it
== aTree
.not_found())
1421 priv
->m_aContentControlAlias
.clear();
1425 priv
->m_aContentControlAlias
= it
->second
.get_value
<std::string
>();
1428 else if (aAction
== "hide")
1430 priv
->m_aContentControlRectangles
.clear();
1431 priv
->m_aContentControlAlias
.clear();
1433 else if (aAction
== "change-picture")
1435 GtkWidget
* pDialog
= gtk_file_chooser_dialog_new(
1436 "Open File", GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView
))),
1437 GTK_FILE_CHOOSER_ACTION_OPEN
, "Cancel", GTK_RESPONSE_CANCEL
, "Open",
1438 GTK_RESPONSE_ACCEPT
, nullptr);
1439 gint nRet
= gtk_dialog_run(GTK_DIALOG(pDialog
));
1440 if (nRet
== GTK_RESPONSE_ACCEPT
)
1442 GtkFileChooser
* pChooser
= GTK_FILE_CHOOSER(pDialog
);
1443 char* pFilename
= gtk_file_chooser_get_uri(pChooser
);
1444 boost::property_tree::ptree aValues
;
1445 aValues
.put("type", "picture");
1446 aValues
.put("changed", pFilename
);
1447 std::stringstream aStream
;
1448 boost::property_tree::write_json(aStream
, aValues
);
1449 std::string aJson
= aStream
.str();
1450 lok_doc_view_send_content_control_event(pDocView
, aJson
.c_str());
1454 gtk_widget_destroy(pDialog
);
1456 g_signal_emit(pCallback
->m_pDocView
, doc_view_signals
[CONTENT_CONTROL
], 0,
1457 pCallback
->m_aPayload
.c_str());
1458 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1462 case LOK_CALLBACK_STATUS_INDICATOR_START
:
1463 case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE
:
1464 case LOK_CALLBACK_STATUS_INDICATOR_FINISH
:
1465 case LOK_CALLBACK_DOCUMENT_PASSWORD
:
1466 case LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY
:
1467 case LOK_CALLBACK_VALIDITY_LIST_BUTTON
:
1468 case LOK_CALLBACK_VALIDITY_INPUT_HELP
:
1469 case LOK_CALLBACK_SIGNATURE_STATUS
:
1470 case LOK_CALLBACK_CONTEXT_MENU
:
1471 case LOK_CALLBACK_PROFILE_FRAME
:
1472 case LOK_CALLBACK_CLIPBOARD_CHANGED
:
1473 case LOK_CALLBACK_CONTEXT_CHANGED
:
1474 case LOK_CALLBACK_CELL_SELECTION_AREA
:
1475 case LOK_CALLBACK_CELL_AUTO_FILL_AREA
:
1476 case LOK_CALLBACK_TABLE_SELECTED
:
1477 case LOK_CALLBACK_JSDIALOG
:
1478 case LOK_CALLBACK_CALC_FUNCTION_LIST
:
1479 case LOK_CALLBACK_TAB_STOP_LIST
:
1480 case LOK_CALLBACK_FORM_FIELD_BUTTON
:
1481 case LOK_CALLBACK_INVALIDATE_SHEET_GEOMETRY
:
1482 case LOK_CALLBACK_DOCUMENT_BACKGROUND_COLOR
:
1483 case LOK_COMMAND_BLOCKED
:
1484 case LOK_CALLBACK_SC_FOLLOW_JUMP
:
1485 case LOK_CALLBACK_PRINT_RANGES
:
1486 case LOK_CALLBACK_FONTS_MISSING
:
1487 case LOK_CALLBACK_MEDIA_SHAPE
:
1488 case LOK_CALLBACK_EXPORT_FILE
:
1489 case LOK_CALLBACK_VIEW_RENDER_STATE
:
1490 case LOK_CALLBACK_APPLICATION_BACKGROUND_COLOR
:
1491 case LOK_CALLBACK_A11Y_FOCUS_CHANGED
:
1492 case LOK_CALLBACK_A11Y_CARET_CHANGED
:
1493 case LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED
:
1494 case LOK_CALLBACK_COLOR_PALETTES
:
1496 // TODO: Implement me
1502 return G_SOURCE_REMOVE
;
1505 static void callbackWorker (int nType
, const char* pPayload
, void* pData
)
1507 LOKDocView
* pDocView
= LOK_DOC_VIEW (pData
);
1509 CallbackData
* pCallback
= new CallbackData(nType
, pPayload
? pPayload
: "(nil)", pDocView
);
1510 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1511 std::stringstream ss
;
1512 ss
<< "callbackWorker, view #" << priv
->m_nViewId
<< ": " << lokCallbackTypeToString(nType
) << ", '" << (pPayload
? pPayload
: "(nil)") << "'";
1513 g_info("%s", ss
.str().c_str());
1514 gdk_threads_add_idle(callback
, pCallback
);
1518 renderHandle(LOKDocView
* pDocView
,
1520 const GdkRectangle
& rCursor
,
1521 cairo_surface_t
* pHandle
,
1522 GdkRectangle
& rRectangle
)
1524 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1525 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
1526 GdkPoint aCursorBottom
;
1527 int nHandleWidth
, nHandleHeight
;
1528 double fHandleScale
;
1530 nHandleWidth
= cairo_image_surface_get_width(pHandle
);
1531 nHandleHeight
= cairo_image_surface_get_height(pHandle
);
1532 // We want to scale down the handle, so that its height is the same as the cursor caret.
1533 fHandleScale
= twipToPixel(rCursor
.height
, priv
->m_fZoom
) / nHandleHeight
;
1534 // We want the top center of the handle bitmap to be at the bottom center of the cursor rectangle.
1535 aCursorBottom
.x
= twipToPixel(rCursor
.x
, priv
->m_fZoom
) + twipToPixel(rCursor
.width
, priv
->m_fZoom
) / 2 - (nHandleWidth
* fHandleScale
) / 2;
1536 aCursorBottom
.y
= twipToPixel(rCursor
.y
, priv
->m_fZoom
) + twipToPixel(rCursor
.height
, priv
->m_fZoom
);
1538 cairo_save (pCairo
);
1539 cairo_scale(pCairo
, 1.0 / nScaleFactor
, 1.0 / nScaleFactor
);
1540 cairo_translate(pCairo
, aCursorBottom
.x
* nScaleFactor
, aCursorBottom
.y
* nScaleFactor
);
1541 cairo_scale(pCairo
, fHandleScale
* nScaleFactor
, fHandleScale
* nScaleFactor
);
1542 cairo_set_source_surface(pCairo
, pHandle
, 0, 0);
1543 cairo_paint(pCairo
);
1544 cairo_restore (pCairo
);
1546 rRectangle
.x
= aCursorBottom
.x
;
1547 rRectangle
.y
= aCursorBottom
.y
;
1548 rRectangle
.width
= nHandleWidth
* fHandleScale
;
1549 rRectangle
.height
= nHandleHeight
* fHandleScale
;
1552 /// Renders handles around an rSelection rectangle on pCairo.
1554 renderGraphicHandle(LOKDocView
* pDocView
,
1556 const GdkRectangle
& rSelection
,
1557 const GdkRGBA
& rColor
)
1559 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1560 int nHandleWidth
= 9, nHandleHeight
= 9;
1561 GdkRectangle aSelection
;
1563 aSelection
.x
= twipToPixel(rSelection
.x
, priv
->m_fZoom
);
1564 aSelection
.y
= twipToPixel(rSelection
.y
, priv
->m_fZoom
);
1565 aSelection
.width
= twipToPixel(rSelection
.width
, priv
->m_fZoom
);
1566 aSelection
.height
= twipToPixel(rSelection
.height
, priv
->m_fZoom
);
1568 for (int i
= 0; i
< GRAPHIC_HANDLE_COUNT
; ++i
)
1570 int x
= aSelection
.x
, y
= aSelection
.y
;
1576 case 1: // top-middle
1577 x
+= aSelection
.width
/ 2;
1579 case 2: // top-right
1580 x
+= aSelection
.width
;
1582 case 3: // middle-left
1583 y
+= aSelection
.height
/ 2;
1585 case 4: // middle-right
1586 x
+= aSelection
.width
;
1587 y
+= aSelection
.height
/ 2;
1589 case 5: // bottom-left
1590 y
+= aSelection
.height
;
1592 case 6: // bottom-middle
1593 x
+= aSelection
.width
/ 2;
1594 y
+= aSelection
.height
;
1596 case 7: // bottom-right
1597 x
+= aSelection
.width
;
1598 y
+= aSelection
.height
;
1602 // Center the handle.
1603 x
-= nHandleWidth
/ 2;
1604 y
-= nHandleHeight
/ 2;
1606 priv
->m_aGraphicHandleRects
[i
].x
= x
;
1607 priv
->m_aGraphicHandleRects
[i
].y
= y
;
1608 priv
->m_aGraphicHandleRects
[i
].width
= nHandleWidth
;
1609 priv
->m_aGraphicHandleRects
[i
].height
= nHandleHeight
;
1611 cairo_set_source_rgb(pCairo
, rColor
.red
, rColor
.green
, rColor
.blue
);
1612 cairo_rectangle(pCairo
, x
, y
, nHandleWidth
, nHandleHeight
);
1617 /// Finishes the paint tile operation and returns the result, if any
1619 paintTileFinish(LOKDocView
* pDocView
, GAsyncResult
* res
, GError
**error
)
1621 GTask
* task
= G_TASK(res
);
1623 g_return_val_if_fail(LOK_IS_DOC_VIEW(pDocView
), nullptr);
1624 g_return_val_if_fail(g_task_is_valid(res
, pDocView
), nullptr);
1625 g_return_val_if_fail(error
== nullptr || *error
== nullptr, nullptr);
1627 return g_task_propagate_pointer(task
, error
);
1630 /// Callback called in the main UI thread when paintTileInThread in LOK thread has finished
1632 paintTileCallback(GObject
* sourceObject
, GAsyncResult
* res
, gpointer userData
)
1634 LOKDocView
* pDocView
= LOK_DOC_VIEW(sourceObject
);
1635 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1636 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(userData
);
1637 std::unique_ptr
<TileBuffer
>& buffer
= priv
->m_pTileBuffer
;
1641 cairo_surface_t
* pSurface
= static_cast<cairo_surface_t
*>(paintTileFinish(pDocView
, res
, &error
));
1642 if (error
!= nullptr)
1644 if (error
->domain
== LOK_TILEBUFFER_ERROR
&&
1645 error
->code
== LOK_TILEBUFFER_CHANGED
)
1646 g_info("Skipping paint tile request because corresponding"
1647 "tile buffer has been destroyed");
1649 g_warning("Unable to get painted GdkPixbuf: %s", error
->message
);
1650 g_error_free(error
);
1654 buffer
->setTile(pLOEvent
->m_nPaintTileX
, pLOEvent
->m_nPaintTileY
, pSurface
);
1655 gdk_threads_add_idle(queueDraw
, GTK_WIDGET(pDocView
));
1657 cairo_surface_destroy(pSurface
);
1662 renderDocument(LOKDocView
* pDocView
, cairo_t
* pCairo
)
1664 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1665 GdkRectangle aVisibleArea
;
1666 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
1667 gint nTileSizePixelsScaled
= nTileSizePixels
* nScaleFactor
;
1668 long nDocumentWidthPixels
= twipToPixel(priv
->m_nDocumentWidthTwips
, priv
->m_fZoom
) * nScaleFactor
;
1669 long nDocumentHeightPixels
= twipToPixel(priv
->m_nDocumentHeightTwips
, priv
->m_fZoom
) * nScaleFactor
;
1670 // Total number of rows / columns in this document.
1671 guint nRows
= ceil(static_cast<double>(nDocumentHeightPixels
) / nTileSizePixelsScaled
);
1672 guint nColumns
= ceil(static_cast<double>(nDocumentWidthPixels
) / nTileSizePixelsScaled
);
1674 cairo_save (pCairo
);
1675 cairo_scale (pCairo
, 1.0/nScaleFactor
, 1.0/nScaleFactor
);
1676 gdk_cairo_get_clip_rectangle (pCairo
, &aVisibleArea
);
1677 aVisibleArea
.x
= pixelToTwip (aVisibleArea
.x
, priv
->m_fZoom
);
1678 aVisibleArea
.y
= pixelToTwip (aVisibleArea
.y
, priv
->m_fZoom
);
1679 aVisibleArea
.width
= pixelToTwip (aVisibleArea
.width
, priv
->m_fZoom
);
1680 aVisibleArea
.height
= pixelToTwip (aVisibleArea
.height
, priv
->m_fZoom
);
1682 // Render the tiles.
1683 for (guint nRow
= 0; nRow
< nRows
; ++nRow
)
1685 for (guint nColumn
= 0; nColumn
< nColumns
; ++nColumn
)
1687 GdkRectangle aTileRectangleTwips
, aTileRectanglePixels
;
1690 // Determine size of the tile: the rightmost/bottommost tiles may
1691 // be smaller, and we need the size to decide if we need to repaint.
1692 if (nColumn
== nColumns
- 1)
1693 aTileRectanglePixels
.width
= nDocumentWidthPixels
- nColumn
* nTileSizePixelsScaled
;
1695 aTileRectanglePixels
.width
= nTileSizePixelsScaled
;
1696 if (nRow
== nRows
- 1)
1697 aTileRectanglePixels
.height
= nDocumentHeightPixels
- nRow
* nTileSizePixelsScaled
;
1699 aTileRectanglePixels
.height
= nTileSizePixelsScaled
;
1701 // Determine size and position of the tile in document coordinates,
1702 // so we can decide if we can skip painting for partial rendering.
1703 aTileRectangleTwips
.x
= pixelToTwip(nTileSizePixelsScaled
, priv
->m_fZoom
) * nColumn
;
1704 aTileRectangleTwips
.y
= pixelToTwip(nTileSizePixelsScaled
, priv
->m_fZoom
) * nRow
;
1705 aTileRectangleTwips
.width
= pixelToTwip(aTileRectanglePixels
.width
, priv
->m_fZoom
);
1706 aTileRectangleTwips
.height
= pixelToTwip(aTileRectanglePixels
.height
, priv
->m_fZoom
);
1708 if (!gdk_rectangle_intersect(&aVisibleArea
, &aTileRectangleTwips
, nullptr))
1713 LOEvent
* pLOEvent
= new LOEvent(LOK_PAINT_TILE
);
1714 pLOEvent
->m_nPaintTileX
= nRow
;
1715 pLOEvent
->m_nPaintTileY
= nColumn
;
1716 pLOEvent
->m_fPaintTileZoom
= priv
->m_fZoom
;
1717 pLOEvent
->m_pTileBuffer
= &*priv
->m_pTileBuffer
;
1718 GTask
* task
= g_task_new(pDocView
, nullptr, paintTileCallback
, pLOEvent
);
1719 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
1721 Tile
& currentTile
= priv
->m_pTileBuffer
->getTile(nRow
, nColumn
, task
, priv
->lokThreadPool
);
1722 cairo_surface_t
* pSurface
= currentTile
.getBuffer();
1723 cairo_set_source_surface(pCairo
, pSurface
,
1724 twipToPixel(aTileRectangleTwips
.x
, priv
->m_fZoom
),
1725 twipToPixel(aTileRectangleTwips
.y
, priv
->m_fZoom
));
1726 cairo_paint(pCairo
);
1727 g_object_unref(task
);
1732 cairo_restore (pCairo
);
1736 static const GdkRGBA
& getDarkColor(int nViewId
, LOKDocViewPrivate
& priv
)
1738 static std::map
<int, GdkRGBA
> aColorMap
;
1739 auto it
= aColorMap
.find(nViewId
);
1740 if (it
!= aColorMap
.end())
1743 if (priv
->m_eDocumentType
== LOK_DOCTYPE_TEXT
)
1745 char* pValues
= priv
->m_pDocument
->pClass
->getCommandValues(priv
->m_pDocument
, ".uno:TrackedChangeAuthors");
1746 std::stringstream aInfo
;
1747 aInfo
<< "lok::Document::getCommandValues('.uno:TrackedChangeAuthors') returned '" << pValues
<< "'" << std::endl
;
1748 g_info("%s", aInfo
.str().c_str());
1750 std::stringstream
aStream(pValues
);
1751 boost::property_tree::ptree aTree
;
1752 boost::property_tree::read_json(aStream
, aTree
);
1753 for (const auto& rValue
: aTree
.get_child("authors"))
1755 const std::string
& rName
= rValue
.second
.get
<std::string
>("name");
1756 guint32 nColor
= rValue
.second
.get
<guint32
>("color");
1757 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};
1758 auto itAuthorViews
= g_aAuthorViews
.find(rName
);
1759 if (itAuthorViews
!= g_aAuthorViews
.end())
1760 aColorMap
[itAuthorViews
->second
] = aColor
;
1765 // Based on tools/color.hxx, COL_AUTHOR1_DARK..COL_AUTHOR9_DARK.
1766 static std::vector
<GdkRGBA
> aColors
=
1768 {(double(198))/255, (double(146))/255, (double(0))/255, 0},
1769 {(double(6))/255, (double(70))/255, (double(162))/255, 0},
1770 {(double(87))/255, (double(157))/255, (double(28))/255, 0},
1771 {(double(105))/255, (double(43))/255, (double(157))/255, 0},
1772 {(double(197))/255, (double(0))/255, (double(11))/255, 0},
1773 {(double(0))/255, (double(128))/255, (double(128))/255, 0},
1774 {(double(140))/255, (double(132))/255, (double(0))/255, 0},
1775 {(double(43))/255, (double(85))/255, (double(107))/255, 0},
1776 {(double(209))/255, (double(118))/255, (double(0))/255, 0},
1778 static int nColorCounter
= 0;
1779 GdkRGBA aColor
= aColors
[nColorCounter
++ % aColors
.size()];
1780 aColorMap
[nViewId
] = aColor
;
1782 assert(aColorMap
.find(nViewId
) != aColorMap
.end());
1783 return aColorMap
[nViewId
];
1787 renderOverlay(LOKDocView
* pDocView
, cairo_t
* pCairo
)
1789 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1791 if (priv
->m_bEdit
&& priv
->m_bCursorVisible
&& priv
->m_bCursorOverlayVisible
&& !isEmptyRectangle(priv
->m_aVisibleCursor
))
1793 if (priv
->m_aVisibleCursor
.width
< 30)
1794 // Set a minimal width if it would be 0.
1795 priv
->m_aVisibleCursor
.width
= 30;
1797 cairo_set_source_rgb(pCairo
, 0, 0, 0);
1798 cairo_rectangle(pCairo
,
1799 twipToPixel(priv
->m_aVisibleCursor
.x
, priv
->m_fZoom
),
1800 twipToPixel(priv
->m_aVisibleCursor
.y
, priv
->m_fZoom
),
1801 twipToPixel(priv
->m_aVisibleCursor
.width
, priv
->m_fZoom
),
1802 twipToPixel(priv
->m_aVisibleCursor
.height
, priv
->m_fZoom
));
1806 // View cursors: they do not blink and are colored.
1807 if (priv
->m_bEdit
&& !priv
->m_aViewCursors
.empty())
1809 for (auto& rPair
: priv
->m_aViewCursors
)
1811 auto itVisibility
= priv
->m_aViewCursorVisibilities
.find(rPair
.first
);
1812 if (itVisibility
!= priv
->m_aViewCursorVisibilities
.end() && !itVisibility
->second
)
1815 // Show view cursors when in Writer or when the part matches.
1816 if (rPair
.second
.m_nPart
!= priv
->m_nPartId
&& priv
->m_eDocumentType
!= LOK_DOCTYPE_TEXT
)
1819 GdkRectangle
& rCursor
= rPair
.second
.m_aRectangle
;
1820 if (rCursor
.width
< 30)
1821 // Set a minimal width if it would be 0.
1824 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
1825 cairo_set_source_rgb(pCairo
, rDark
.red
, rDark
.green
, rDark
.blue
);
1826 cairo_rectangle(pCairo
,
1827 twipToPixel(rCursor
.x
, priv
->m_fZoom
),
1828 twipToPixel(rCursor
.y
, priv
->m_fZoom
),
1829 twipToPixel(rCursor
.width
, priv
->m_fZoom
),
1830 twipToPixel(rCursor
.height
, priv
->m_fZoom
));
1835 if (priv
->m_bEdit
&& priv
->m_bCursorVisible
&& !isEmptyRectangle(priv
->m_aVisibleCursor
) && priv
->m_aTextSelectionRectangles
.empty())
1837 // Have a cursor, but no selection: we need the middle handle.
1838 gchar
* handleMiddlePath
= g_strconcat (priv
->m_aLOPath
.c_str(), CURSOR_HANDLE_DIR
, "handle_image_middle.png", nullptr);
1839 if (!priv
->m_pHandleMiddle
)
1841 priv
->m_pHandleMiddle
= cairo_image_surface_create_from_png(handleMiddlePath
);
1842 assert(cairo_surface_status(priv
->m_pHandleMiddle
) == CAIRO_STATUS_SUCCESS
);
1844 g_free (handleMiddlePath
);
1845 renderHandle(pDocView
, pCairo
, priv
->m_aVisibleCursor
, priv
->m_pHandleMiddle
, priv
->m_aHandleMiddleRect
);
1848 if (!priv
->m_aTextSelectionRectangles
.empty())
1850 for (const GdkRectangle
& rRectangle
: priv
->m_aTextSelectionRectangles
)
1852 // Blue with 75% transparency.
1853 cairo_set_source_rgba(pCairo
, (double(0x43))/255, (double(0xac))/255, (double(0xe8))/255, 0.25);
1854 cairo_rectangle(pCairo
,
1855 twipToPixel(rRectangle
.x
, priv
->m_fZoom
),
1856 twipToPixel(rRectangle
.y
, priv
->m_fZoom
),
1857 twipToPixel(rRectangle
.width
, priv
->m_fZoom
),
1858 twipToPixel(rRectangle
.height
, priv
->m_fZoom
));
1863 if (!isEmptyRectangle(priv
->m_aTextSelectionStart
))
1865 // Have a start position: we need a start handle.
1866 gchar
* handleStartPath
= g_strconcat (priv
->m_aLOPath
.c_str(), CURSOR_HANDLE_DIR
, "handle_image_start.png", nullptr);
1867 if (!priv
->m_pHandleStart
)
1869 priv
->m_pHandleStart
= cairo_image_surface_create_from_png(handleStartPath
);
1870 assert(cairo_surface_status(priv
->m_pHandleStart
) == CAIRO_STATUS_SUCCESS
);
1872 renderHandle(pDocView
, pCairo
, priv
->m_aTextSelectionStart
, priv
->m_pHandleStart
, priv
->m_aHandleStartRect
);
1873 g_free (handleStartPath
);
1875 if (!isEmptyRectangle(priv
->m_aTextSelectionEnd
))
1877 // Have a start position: we need an end handle.
1878 gchar
* handleEndPath
= g_strconcat (priv
->m_aLOPath
.c_str(), CURSOR_HANDLE_DIR
, "handle_image_end.png", nullptr);
1879 if (!priv
->m_pHandleEnd
)
1881 priv
->m_pHandleEnd
= cairo_image_surface_create_from_png(handleEndPath
);
1882 assert(cairo_surface_status(priv
->m_pHandleEnd
) == CAIRO_STATUS_SUCCESS
);
1884 renderHandle(pDocView
, pCairo
, priv
->m_aTextSelectionEnd
, priv
->m_pHandleEnd
, priv
->m_aHandleEndRect
);
1885 g_free (handleEndPath
);
1889 if (!priv
->m_aContentControlRectangles
.empty())
1891 for (const GdkRectangle
& rRectangle
: priv
->m_aContentControlRectangles
)
1893 // Black with 75% transparency.
1894 cairo_set_source_rgba(pCairo
, (double(0x7f))/255, (double(0x7f))/255, (double(0x7f))/255, 0.25);
1895 cairo_rectangle(pCairo
,
1896 twipToPixel(rRectangle
.x
, priv
->m_fZoom
),
1897 twipToPixel(rRectangle
.y
, priv
->m_fZoom
),
1898 twipToPixel(rRectangle
.width
, priv
->m_fZoom
),
1899 twipToPixel(rRectangle
.height
, priv
->m_fZoom
));
1903 if (!priv
->m_aContentControlAlias
.empty())
1905 cairo_text_extents_t aExtents
;
1906 cairo_text_extents(pCairo
, priv
->m_aContentControlAlias
.c_str(), &aExtents
);
1907 // Blue with 75% transparency.
1908 cairo_set_source_rgba(pCairo
, 0, 0, 1, 0.25);
1909 cairo_rectangle(pCairo
,
1910 twipToPixel(priv
->m_aContentControlRectangles
[0].x
, priv
->m_fZoom
) + aExtents
.x_bearing
,
1911 twipToPixel(priv
->m_aContentControlRectangles
[0].y
, priv
->m_fZoom
) + aExtents
.y_bearing
,
1916 cairo_move_to(pCairo
,
1917 twipToPixel(priv
->m_aContentControlRectangles
[0].x
, priv
->m_fZoom
),
1918 twipToPixel(priv
->m_aContentControlRectangles
[0].y
, priv
->m_fZoom
));
1919 cairo_set_source_rgb(pCairo
, 0, 0, 0);
1920 cairo_show_text(pCairo
, priv
->m_aContentControlAlias
.c_str());
1925 // Selections of other views.
1926 for (const auto& rPair
: priv
->m_aTextViewSelectionRectangles
)
1928 if (rPair
.second
.m_nPart
!= priv
->m_nPartId
&& priv
->m_eDocumentType
!= LOK_DOCTYPE_TEXT
)
1931 for (const GdkRectangle
& rRectangle
: rPair
.second
.m_aRectangles
)
1933 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
1934 // 75% transparency.
1935 cairo_set_source_rgba(pCairo
, rDark
.red
, rDark
.green
, rDark
.blue
, 0.25);
1936 cairo_rectangle(pCairo
,
1937 twipToPixel(rRectangle
.x
, priv
->m_fZoom
),
1938 twipToPixel(rRectangle
.y
, priv
->m_fZoom
),
1939 twipToPixel(rRectangle
.width
, priv
->m_fZoom
),
1940 twipToPixel(rRectangle
.height
, priv
->m_fZoom
));
1945 if (!isEmptyRectangle(priv
->m_aGraphicSelection
))
1947 GdkRGBA
const aBlack
{0, 0, 0, 0};
1948 renderGraphicHandle(pDocView
, pCairo
, priv
->m_aGraphicSelection
, aBlack
);
1951 // Graphic selections of other views.
1952 for (const auto& rPair
: priv
->m_aGraphicViewSelections
)
1954 const ViewRectangle
& rRectangle
= rPair
.second
;
1955 if (rRectangle
.m_nPart
!= priv
->m_nPartId
&& priv
->m_eDocumentType
!= LOK_DOCTYPE_TEXT
)
1958 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
1959 renderGraphicHandle(pDocView
, pCairo
, rRectangle
.m_aRectangle
, rDark
);
1962 // Draw the cell cursor.
1963 if (!isEmptyRectangle(priv
->m_aCellCursor
))
1965 cairo_set_source_rgb(pCairo
, 0, 0, 0);
1966 cairo_rectangle(pCairo
,
1967 twipToPixel(priv
->m_aCellCursor
.x
, priv
->m_fZoom
),
1968 twipToPixel(priv
->m_aCellCursor
.y
, priv
->m_fZoom
),
1969 twipToPixel(priv
->m_aCellCursor
.width
, priv
->m_fZoom
),
1970 twipToPixel(priv
->m_aCellCursor
.height
, priv
->m_fZoom
));
1971 cairo_set_line_width(pCairo
, 2.0);
1972 cairo_stroke(pCairo
);
1975 // Cell view cursors: they are colored.
1976 for (const auto& rPair
: priv
->m_aCellViewCursors
)
1978 const ViewRectangle
& rCursor
= rPair
.second
;
1979 if (rCursor
.m_nPart
!= priv
->m_nPartId
)
1982 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
1983 cairo_set_source_rgb(pCairo
, rDark
.red
, rDark
.green
, rDark
.blue
);
1984 cairo_rectangle(pCairo
,
1985 twipToPixel(rCursor
.m_aRectangle
.x
, priv
->m_fZoom
),
1986 twipToPixel(rCursor
.m_aRectangle
.y
, priv
->m_fZoom
),
1987 twipToPixel(rCursor
.m_aRectangle
.width
, priv
->m_fZoom
),
1988 twipToPixel(rCursor
.m_aRectangle
.height
, priv
->m_fZoom
));
1989 cairo_set_line_width(pCairo
, 2.0);
1990 cairo_stroke(pCairo
);
1993 // Draw reference marks.
1994 for (const auto& rPair
: priv
->m_aReferenceMarks
)
1996 const ViewRectangle
& rMark
= rPair
.first
;
1997 if (rMark
.m_nPart
!= priv
->m_nPartId
)
2000 sal_uInt32 nColor
= rPair
.second
;
2001 sal_uInt8 nRed
= (nColor
>> 16) & 0xff;
2002 sal_uInt8 nGreen
= (nColor
>> 8) & 0xff;
2003 sal_uInt8 nBlue
= nColor
& 0xff;
2004 cairo_set_source_rgb(pCairo
, nRed
, nGreen
, nBlue
);
2005 cairo_rectangle(pCairo
,
2006 twipToPixel(rMark
.m_aRectangle
.x
, priv
->m_fZoom
),
2007 twipToPixel(rMark
.m_aRectangle
.y
, priv
->m_fZoom
),
2008 twipToPixel(rMark
.m_aRectangle
.width
, priv
->m_fZoom
),
2009 twipToPixel(rMark
.m_aRectangle
.height
, priv
->m_fZoom
));
2010 cairo_set_line_width(pCairo
, 2.0);
2011 cairo_stroke(pCairo
);
2014 // View locks: they are colored.
2015 for (const auto& rPair
: priv
->m_aViewLockRectangles
)
2017 const ViewRectangle
& rRectangle
= rPair
.second
;
2018 if (rRectangle
.m_nPart
!= priv
->m_nPartId
)
2021 // Draw a rectangle.
2022 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
2023 cairo_set_source_rgb(pCairo
, rDark
.red
, rDark
.green
, rDark
.blue
);
2024 cairo_rectangle(pCairo
,
2025 twipToPixel(rRectangle
.m_aRectangle
.x
, priv
->m_fZoom
),
2026 twipToPixel(rRectangle
.m_aRectangle
.y
, priv
->m_fZoom
),
2027 twipToPixel(rRectangle
.m_aRectangle
.width
, priv
->m_fZoom
),
2028 twipToPixel(rRectangle
.m_aRectangle
.height
, priv
->m_fZoom
));
2029 cairo_set_line_width(pCairo
, 2.0);
2030 cairo_stroke(pCairo
);
2033 cairo_rectangle(pCairo
,
2034 twipToPixel(rRectangle
.m_aRectangle
.x
+ rRectangle
.m_aRectangle
.width
, priv
->m_fZoom
) - 25,
2035 twipToPixel(rRectangle
.m_aRectangle
.y
+ rRectangle
.m_aRectangle
.height
, priv
->m_fZoom
) - 15,
2040 twipToPixel(rRectangle
.m_aRectangle
.x
+ rRectangle
.m_aRectangle
.width
, priv
->m_fZoom
) - 15,
2041 twipToPixel(rRectangle
.m_aRectangle
.y
+ rRectangle
.m_aRectangle
.height
, priv
->m_fZoom
) - 15,
2045 cairo_stroke(pCairo
);
2052 lok_doc_view_signal_button(GtkWidget
* pWidget
, GdkEventButton
* pEvent
)
2054 LOKDocView
* pDocView
= LOK_DOC_VIEW (pWidget
);
2055 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2056 GError
* error
= nullptr;
2058 g_info("LOKDocView_Impl::signalButton: %d, %d (in twips: %d, %d)",
2059 static_cast<int>(pEvent
->x
), static_cast<int>(pEvent
->y
),
2060 static_cast<int>(pixelToTwip(pEvent
->x
, priv
->m_fZoom
)),
2061 static_cast<int>(pixelToTwip(pEvent
->y
, priv
->m_fZoom
)));
2062 gtk_widget_grab_focus(GTK_WIDGET(pDocView
));
2064 switch (pEvent
->type
)
2066 case GDK_BUTTON_PRESS
:
2068 GdkRectangle aClick
;
2069 aClick
.x
= pEvent
->x
;
2070 aClick
.y
= pEvent
->y
;
2074 if (handleTextSelectionOnButtonPress(aClick
, pDocView
))
2076 if (handleGraphicSelectionOnButtonPress(aClick
, pDocView
))
2080 if ((pEvent
->time
- priv
->m_nLastButtonPressTime
) < 250)
2082 priv
->m_nLastButtonPressTime
= pEvent
->time
;
2083 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
2084 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_MOUSE_EVENT
);
2085 pLOEvent
->m_nPostMouseEventType
= LOK_MOUSEEVENT_MOUSEBUTTONDOWN
;
2086 pLOEvent
->m_nPostMouseEventX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
2087 pLOEvent
->m_nPostMouseEventY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
2088 pLOEvent
->m_nPostMouseEventCount
= nCount
;
2089 switch (pEvent
->button
)
2092 pLOEvent
->m_nPostMouseEventButton
= MOUSE_LEFT
;
2095 pLOEvent
->m_nPostMouseEventButton
= MOUSE_MIDDLE
;
2098 pLOEvent
->m_nPostMouseEventButton
= MOUSE_RIGHT
;
2101 pLOEvent
->m_nPostMouseEventModifier
= priv
->m_nKeyModifier
;
2102 priv
->m_nLastButtonPressed
= pLOEvent
->m_nPostMouseEventButton
;
2103 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
2105 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
2106 if (error
!= nullptr)
2108 g_warning("Unable to call LOK_POST_MOUSE_EVENT: %s", error
->message
);
2109 g_clear_error(&error
);
2111 g_object_unref(task
);
2114 case GDK_BUTTON_RELEASE
:
2116 if (handleTextSelectionOnButtonRelease(pDocView
))
2118 if (handleGraphicSelectionOnButtonRelease(pDocView
, pEvent
))
2122 if ((pEvent
->time
- priv
->m_nLastButtonReleaseTime
) < 250)
2124 priv
->m_nLastButtonReleaseTime
= pEvent
->time
;
2125 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
2126 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_MOUSE_EVENT
);
2127 pLOEvent
->m_nPostMouseEventType
= LOK_MOUSEEVENT_MOUSEBUTTONUP
;
2128 pLOEvent
->m_nPostMouseEventX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
2129 pLOEvent
->m_nPostMouseEventY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
2130 pLOEvent
->m_nPostMouseEventCount
= nCount
;
2131 switch (pEvent
->button
)
2134 pLOEvent
->m_nPostMouseEventButton
= MOUSE_LEFT
;
2137 pLOEvent
->m_nPostMouseEventButton
= MOUSE_MIDDLE
;
2140 pLOEvent
->m_nPostMouseEventButton
= MOUSE_RIGHT
;
2143 pLOEvent
->m_nPostMouseEventModifier
= priv
->m_nKeyModifier
;
2144 priv
->m_nLastButtonPressed
= pLOEvent
->m_nPostMouseEventButton
;
2145 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
2147 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
2148 if (error
!= nullptr)
2150 g_warning("Unable to call LOK_POST_MOUSE_EVENT: %s", error
->message
);
2151 g_clear_error(&error
);
2153 g_object_unref(task
);
2163 getDragPoint(GdkRectangle
* pHandle
,
2164 GdkEventMotion
* pEvent
,
2167 GdkPoint aCursor
, aHandle
;
2169 // Center of the cursor rectangle: we know that it's above the handle.
2170 aCursor
.x
= pHandle
->x
+ pHandle
->width
/ 2;
2171 aCursor
.y
= pHandle
->y
- pHandle
->height
/ 2;
2172 // Center of the handle rectangle.
2173 aHandle
.x
= pHandle
->x
+ pHandle
->width
/ 2;
2174 aHandle
.y
= pHandle
->y
+ pHandle
->height
/ 2;
2175 // Our target is the original cursor position + the dragged offset.
2176 pPoint
->x
= aCursor
.x
+ (pEvent
->x
- aHandle
.x
);
2177 pPoint
->y
= aCursor
.y
+ (pEvent
->y
- aHandle
.y
);
2181 lok_doc_view_signal_motion (GtkWidget
* pWidget
, GdkEventMotion
* pEvent
)
2183 LOKDocView
* pDocView
= LOK_DOC_VIEW (pWidget
);
2184 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2186 GError
* error
= nullptr;
2188 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2189 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
2190 if (priv
->m_bInDragMiddleHandle
)
2192 g_info("lcl_signalMotion: dragging the middle handle");
2193 getDragPoint(&priv
->m_aHandleMiddleRect
, pEvent
, &aPoint
);
2194 priv
->m_pDocument
->pClass
->setTextSelection(priv
->m_pDocument
, LOK_SETTEXTSELECTION_RESET
, pixelToTwip(aPoint
.x
, priv
->m_fZoom
), pixelToTwip(aPoint
.y
, priv
->m_fZoom
));
2197 if (priv
->m_bInDragStartHandle
)
2199 g_info("lcl_signalMotion: dragging the start handle");
2200 getDragPoint(&priv
->m_aHandleStartRect
, pEvent
, &aPoint
);
2201 priv
->m_pDocument
->pClass
->setTextSelection(priv
->m_pDocument
, LOK_SETTEXTSELECTION_START
, pixelToTwip(aPoint
.x
, priv
->m_fZoom
), pixelToTwip(aPoint
.y
, priv
->m_fZoom
));
2204 if (priv
->m_bInDragEndHandle
)
2206 g_info("lcl_signalMotion: dragging the end handle");
2207 getDragPoint(&priv
->m_aHandleEndRect
, pEvent
, &aPoint
);
2208 priv
->m_pDocument
->pClass
->setTextSelection(priv
->m_pDocument
, LOK_SETTEXTSELECTION_END
, pixelToTwip(aPoint
.x
, priv
->m_fZoom
), pixelToTwip(aPoint
.y
, priv
->m_fZoom
));
2212 for (int i
= 0; i
< GRAPHIC_HANDLE_COUNT
; ++i
)
2214 if (priv
->m_bInDragGraphicHandles
[i
])
2216 g_info("lcl_signalMotion: dragging the graphic handle #%d", i
);
2220 if (priv
->m_bInDragGraphicSelection
)
2222 g_info("lcl_signalMotion: dragging the graphic selection");
2226 GdkRectangle aMotionInTwipsInTwips
;
2227 aMotionInTwipsInTwips
.x
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
2228 aMotionInTwipsInTwips
.y
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
2229 aMotionInTwipsInTwips
.width
= 1;
2230 aMotionInTwipsInTwips
.height
= 1;
2231 if (gdk_rectangle_intersect(&aMotionInTwipsInTwips
, &priv
->m_aGraphicSelection
, nullptr))
2233 g_info("lcl_signalMotion: start of drag graphic selection");
2234 priv
->m_bInDragGraphicSelection
= true;
2236 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
2237 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_GRAPHIC_SELECTION
);
2238 pLOEvent
->m_nSetGraphicSelectionType
= LOK_SETGRAPHICSELECTION_START
;
2239 pLOEvent
->m_nSetGraphicSelectionX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
2240 pLOEvent
->m_nSetGraphicSelectionY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
2241 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
2243 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
2244 if (error
!= nullptr)
2246 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error
->message
);
2247 g_clear_error(&error
);
2249 g_object_unref(task
);
2254 // Otherwise a mouse move, as on the desktop.
2256 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
2257 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_MOUSE_EVENT
);
2258 pLOEvent
->m_nPostMouseEventType
= LOK_MOUSEEVENT_MOUSEMOVE
;
2259 pLOEvent
->m_nPostMouseEventX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
2260 pLOEvent
->m_nPostMouseEventY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
2261 pLOEvent
->m_nPostMouseEventCount
= 1;
2262 pLOEvent
->m_nPostMouseEventButton
= priv
->m_nLastButtonPressed
;
2263 pLOEvent
->m_nPostMouseEventModifier
= priv
->m_nKeyModifier
;
2265 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
2267 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
2268 if (error
!= nullptr)
2270 g_warning("Unable to call LOK_MOUSEEVENT_MOUSEMOVE: %s", error
->message
);
2271 g_clear_error(&error
);
2273 g_object_unref(task
);
2279 setGraphicSelectionInThread(gpointer data
)
2281 GTask
* task
= G_TASK(data
);
2282 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2283 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2284 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2286 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2287 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
2288 std::stringstream ss
;
2289 ss
<< "lok::Document::setGraphicSelection(" << pLOEvent
->m_nSetGraphicSelectionType
;
2290 ss
<< ", " << pLOEvent
->m_nSetGraphicSelectionX
;
2291 ss
<< ", " << pLOEvent
->m_nSetGraphicSelectionY
<< ")";
2292 g_info("%s", ss
.str().c_str());
2293 priv
->m_pDocument
->pClass
->setGraphicSelection(priv
->m_pDocument
,
2294 pLOEvent
->m_nSetGraphicSelectionType
,
2295 pLOEvent
->m_nSetGraphicSelectionX
,
2296 pLOEvent
->m_nSetGraphicSelectionY
);
2300 setClientZoomInThread(gpointer data
)
2302 GTask
* task
= G_TASK(data
);
2303 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2304 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2305 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2307 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2308 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
2309 priv
->m_pDocument
->pClass
->setClientZoom(priv
->m_pDocument
,
2310 pLOEvent
->m_nTilePixelWidth
,
2311 pLOEvent
->m_nTilePixelHeight
,
2312 pLOEvent
->m_nTileTwipWidth
,
2313 pLOEvent
->m_nTileTwipHeight
);
2317 postMouseEventInThread(gpointer data
)
2319 GTask
* task
= G_TASK(data
);
2320 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2321 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2322 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2324 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2325 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
2326 std::stringstream ss
;
2327 ss
<< "lok::Document::postMouseEvent(" << pLOEvent
->m_nPostMouseEventType
;
2328 ss
<< ", " << pLOEvent
->m_nPostMouseEventX
;
2329 ss
<< ", " << pLOEvent
->m_nPostMouseEventY
;
2330 ss
<< ", " << pLOEvent
->m_nPostMouseEventCount
;
2331 ss
<< ", " << pLOEvent
->m_nPostMouseEventButton
;
2332 ss
<< ", " << pLOEvent
->m_nPostMouseEventModifier
<< ")";
2333 g_info("%s", ss
.str().c_str());
2334 priv
->m_pDocument
->pClass
->postMouseEvent(priv
->m_pDocument
,
2335 pLOEvent
->m_nPostMouseEventType
,
2336 pLOEvent
->m_nPostMouseEventX
,
2337 pLOEvent
->m_nPostMouseEventY
,
2338 pLOEvent
->m_nPostMouseEventCount
,
2339 pLOEvent
->m_nPostMouseEventButton
,
2340 pLOEvent
->m_nPostMouseEventModifier
);
2344 openDocumentInThread (gpointer data
)
2346 GTask
* task
= G_TASK(data
);
2347 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2348 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2350 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2351 if ( priv
->m_pDocument
)
2353 priv
->m_pDocument
->pClass
->destroy( priv
->m_pDocument
);
2354 priv
->m_pDocument
= nullptr;
2357 priv
->m_pOffice
->pClass
->registerCallback(priv
->m_pOffice
, globalCallbackWorker
, pDocView
);
2358 std::string url
= priv
->m_aDocPath
;
2359 if (gchar
* pURL
= g_filename_to_uri(url
.c_str(), nullptr, nullptr))
2364 priv
->m_pDocument
= priv
->m_pOffice
->pClass
->documentLoadWithOptions( priv
->m_pOffice
, url
.c_str(), "en-US" );
2365 if ( !priv
->m_pDocument
)
2367 char *pError
= priv
->m_pOffice
->pClass
->getError( priv
->m_pOffice
);
2368 g_task_return_new_error(task
, g_quark_from_static_string ("LOK error"), 0, "%s", pError
);
2372 priv
->m_eDocumentType
= static_cast<LibreOfficeKitDocumentType
>(priv
->m_pDocument
->pClass
->getDocumentType(priv
->m_pDocument
));
2373 gdk_threads_add_idle(postDocumentLoad
, pDocView
);
2374 g_task_return_boolean (task
, true);
2379 setPartInThread(gpointer data
)
2381 GTask
* task
= G_TASK(data
);
2382 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2383 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2384 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2385 int nPart
= pLOEvent
->m_nPart
;
2387 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2388 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
2389 priv
->m_pDocument
->pClass
->setPart( priv
->m_pDocument
, nPart
);
2392 lok_doc_view_reset_view(pDocView
);
2396 setPartmodeInThread(gpointer data
)
2398 GTask
* task
= G_TASK(data
);
2399 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2400 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2401 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2402 int nPartMode
= pLOEvent
->m_nPartMode
;
2404 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2405 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
2406 priv
->m_pDocument
->pClass
->setPartMode( priv
->m_pDocument
, nPartMode
);
2410 setEditInThread(gpointer data
)
2412 GTask
* task
= G_TASK(data
);
2413 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2414 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2415 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2416 bool bWasEdit
= priv
->m_bEdit
;
2417 bool bEdit
= pLOEvent
->m_bEdit
;
2419 if (!priv
->m_bEdit
&& bEdit
)
2420 g_info("lok_doc_view_set_edit: entering edit mode");
2421 else if (priv
->m_bEdit
&& !bEdit
)
2423 g_info("lok_doc_view_set_edit: leaving edit mode");
2424 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2425 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
2426 priv
->m_pDocument
->pClass
->resetSelection(priv
->m_pDocument
);
2428 priv
->m_bEdit
= bEdit
;
2429 g_signal_emit(pDocView
, doc_view_signals
[EDIT_CHANGED
], 0, bWasEdit
);
2430 gdk_threads_add_idle(queueDraw
, GTK_WIDGET(pDocView
));
2434 postCommandInThread (gpointer data
)
2436 GTask
* task
= G_TASK(data
);
2437 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2438 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2439 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2441 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2442 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
2443 std::stringstream ss
;
2444 ss
<< "lok::Document::postUnoCommand(" << pLOEvent
->m_pCommand
<< ", " << pLOEvent
->m_pArguments
<< ")";
2445 g_info("%s", ss
.str().c_str());
2446 priv
->m_pDocument
->pClass
->postUnoCommand(priv
->m_pDocument
, pLOEvent
->m_pCommand
, pLOEvent
->m_pArguments
, pLOEvent
->m_bNotifyWhenFinished
);
2450 paintTileInThread (gpointer data
)
2452 GTask
* task
= G_TASK(data
);
2453 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2454 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2455 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2456 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
2457 gint nTileSizePixelsScaled
= nTileSizePixels
* nScaleFactor
;
2459 // check if "source" tile buffer is different from "current" tile buffer
2460 if (pLOEvent
->m_pTileBuffer
!= &*priv
->m_pTileBuffer
)
2462 pLOEvent
->m_pTileBuffer
= nullptr;
2463 g_task_return_new_error(task
,
2464 LOK_TILEBUFFER_ERROR
,
2465 LOK_TILEBUFFER_CHANGED
,
2466 "TileBuffer has changed");
2469 std::unique_ptr
<TileBuffer
>& buffer
= priv
->m_pTileBuffer
;
2470 if (buffer
->hasValidTile(pLOEvent
->m_nPaintTileX
, pLOEvent
->m_nPaintTileY
))
2473 cairo_surface_t
*pSurface
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32
, nTileSizePixelsScaled
, nTileSizePixelsScaled
);
2474 if (cairo_surface_status(pSurface
) != CAIRO_STATUS_SUCCESS
)
2476 cairo_surface_destroy(pSurface
);
2477 g_task_return_new_error(task
,
2478 LOK_TILEBUFFER_ERROR
,
2479 LOK_TILEBUFFER_MEMORY
,
2480 "Error allocating Surface");
2484 unsigned char* pBuffer
= cairo_image_surface_get_data(pSurface
);
2485 GdkRectangle aTileRectangle
;
2486 aTileRectangle
.x
= pixelToTwip(nTileSizePixelsScaled
, pLOEvent
->m_fPaintTileZoom
* nScaleFactor
) * pLOEvent
->m_nPaintTileY
;
2487 aTileRectangle
.y
= pixelToTwip(nTileSizePixelsScaled
, pLOEvent
->m_fPaintTileZoom
* nScaleFactor
) * pLOEvent
->m_nPaintTileX
;
2489 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2490 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
2491 std::stringstream ss
;
2492 GTimer
* aTimer
= g_timer_new();
2494 ss
<< "lok::Document::paintTile(" << static_cast<void*>(pBuffer
) << ", "
2495 << nTileSizePixelsScaled
<< ", " << nTileSizePixelsScaled
<< ", "
2496 << aTileRectangle
.x
<< ", " << aTileRectangle
.y
<< ", "
2497 << pixelToTwip(nTileSizePixelsScaled
, pLOEvent
->m_fPaintTileZoom
* nScaleFactor
) << ", "
2498 << pixelToTwip(nTileSizePixelsScaled
, pLOEvent
->m_fPaintTileZoom
* nScaleFactor
) << ")";
2500 priv
->m_pDocument
->pClass
->paintTile(priv
->m_pDocument
,
2502 nTileSizePixelsScaled
, nTileSizePixelsScaled
,
2503 aTileRectangle
.x
, aTileRectangle
.y
,
2504 pixelToTwip(nTileSizePixelsScaled
, pLOEvent
->m_fPaintTileZoom
* nScaleFactor
),
2505 pixelToTwip(nTileSizePixelsScaled
, pLOEvent
->m_fPaintTileZoom
* nScaleFactor
));
2508 g_timer_elapsed(aTimer
, &nElapsedMs
);
2509 ss
<< " rendered in " << (nElapsedMs
/ 1000.) << " milliseconds";
2510 g_info("%s", ss
.str().c_str());
2511 g_timer_destroy(aTimer
);
2513 cairo_surface_mark_dirty(pSurface
);
2515 // Its likely that while the tilebuffer has changed, one of the paint tile
2516 // requests has passed the previous check at start of this function, and has
2517 // rendered the tile already. We want to stop such rendered tiles from being
2518 // stored in new tile buffer.
2519 if (pLOEvent
->m_pTileBuffer
!= &*priv
->m_pTileBuffer
)
2521 pLOEvent
->m_pTileBuffer
= nullptr;
2522 g_task_return_new_error(task
,
2523 LOK_TILEBUFFER_ERROR
,
2524 LOK_TILEBUFFER_CHANGED
,
2525 "TileBuffer has changed");
2529 g_task_return_pointer(task
, pSurface
, reinterpret_cast<GDestroyNotify
>(cairo_surface_destroy
));
2534 lokThreadFunc(gpointer data
, gpointer
/*user_data*/)
2536 GTask
* task
= G_TASK(data
);
2537 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2538 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2539 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2541 switch (pLOEvent
->m_nType
)
2544 openDocumentInThread(task
);
2546 case LOK_POST_COMMAND
:
2547 postCommandInThread(task
);
2550 setEditInThread(task
);
2553 setPartInThread(task
);
2555 case LOK_SET_PARTMODE
:
2556 setPartmodeInThread(task
);
2559 // view-only/editable mode already checked during signal key signal emission
2560 postKeyEventInThread(task
);
2562 case LOK_PAINT_TILE
:
2563 paintTileInThread(task
);
2565 case LOK_POST_MOUSE_EVENT
:
2566 postMouseEventInThread(task
);
2568 case LOK_SET_GRAPHIC_SELECTION
:
2570 setGraphicSelectionInThread(task
);
2572 g_info ("LOK_SET_GRAPHIC_SELECTION: skipping graphical operation in view-only mode");
2574 case LOK_SET_CLIENT_ZOOM
:
2575 setClientZoomInThread(task
);
2579 g_object_unref(task
);
2583 onStyleContextChanged (LOKDocView
* pDocView
)
2585 // The scale factor might have changed
2586 updateClientZoom (pDocView
);
2587 gtk_widget_queue_draw (GTK_WIDGET (pDocView
));
2590 static void lok_doc_view_init (LOKDocView
* pDocView
)
2592 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2593 priv
.m_pImpl
= new LOKDocViewPrivateImpl();
2595 gtk_widget_add_events(GTK_WIDGET(pDocView
),
2596 GDK_BUTTON_PRESS_MASK
2597 |GDK_BUTTON_RELEASE_MASK
2598 |GDK_BUTTON_MOTION_MASK
2600 |GDK_KEY_RELEASE_MASK
);
2602 priv
->lokThreadPool
= g_thread_pool_new(lokThreadFunc
,
2608 g_signal_connect (pDocView
, "style-updated", G_CALLBACK(onStyleContextChanged
), nullptr);
2611 static void lok_doc_view_set_property (GObject
* object
, guint propId
, const GValue
*value
, GParamSpec
*pspec
)
2613 LOKDocView
* pDocView
= LOK_DOC_VIEW (object
);
2614 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2615 bool bDocPasswordEnabled
= priv
->m_nLOKFeatures
& LOK_FEATURE_DOCUMENT_PASSWORD
;
2616 bool bDocPasswordToModifyEnabled
= priv
->m_nLOKFeatures
& LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY
;
2617 bool bTiledAnnotationsEnabled
= !(priv
->m_nLOKFeatures
& LOK_FEATURE_NO_TILED_ANNOTATIONS
);
2622 priv
->m_aLOPath
= g_value_get_string (value
);
2624 case PROP_LO_UNIPOLL
:
2625 priv
->m_bUnipoll
= g_value_get_boolean (value
);
2627 case PROP_LO_POINTER
:
2628 priv
->m_pOffice
= static_cast<LibreOfficeKit
*>(g_value_get_pointer(value
));
2630 case PROP_USER_PROFILE_URL
:
2631 if (const gchar
* pUserProfile
= g_value_get_string(value
))
2632 priv
->m_aUserProfileURL
= pUserProfile
;
2635 priv
->m_aDocPath
= g_value_get_string (value
);
2637 case PROP_DOC_POINTER
:
2638 priv
->m_pDocument
= static_cast<LibreOfficeKitDocument
*>(g_value_get_pointer(value
));
2639 priv
->m_eDocumentType
= static_cast<LibreOfficeKitDocumentType
>(priv
->m_pDocument
->pClass
->getDocumentType(priv
->m_pDocument
));
2642 lok_doc_view_set_edit (pDocView
, g_value_get_boolean (value
));
2645 lok_doc_view_set_zoom (pDocView
, g_value_get_float (value
));
2647 case PROP_DOC_WIDTH
:
2648 priv
->m_nDocumentWidthTwips
= g_value_get_long (value
);
2650 case PROP_DOC_HEIGHT
:
2651 priv
->m_nDocumentHeightTwips
= g_value_get_long (value
);
2653 case PROP_DOC_PASSWORD
:
2654 if (bool(g_value_get_boolean (value
)) != bDocPasswordEnabled
)
2656 priv
->m_nLOKFeatures
= priv
->m_nLOKFeatures
^ LOK_FEATURE_DOCUMENT_PASSWORD
;
2657 priv
->m_pOffice
->pClass
->setOptionalFeatures(priv
->m_pOffice
, priv
->m_nLOKFeatures
);
2660 case PROP_DOC_PASSWORD_TO_MODIFY
:
2661 if ( bool(g_value_get_boolean (value
)) != bDocPasswordToModifyEnabled
)
2663 priv
->m_nLOKFeatures
= priv
->m_nLOKFeatures
^ LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY
;
2664 priv
->m_pOffice
->pClass
->setOptionalFeatures(priv
->m_pOffice
, priv
->m_nLOKFeatures
);
2667 case PROP_TILED_ANNOTATIONS
:
2668 if ( bool(g_value_get_boolean (value
)) != bTiledAnnotationsEnabled
)
2670 priv
->m_nLOKFeatures
= priv
->m_nLOKFeatures
^ LOK_FEATURE_NO_TILED_ANNOTATIONS
;
2671 priv
->m_pOffice
->pClass
->setOptionalFeatures(priv
->m_pOffice
, priv
->m_nLOKFeatures
);
2675 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, propId
, pspec
);
2679 static void lok_doc_view_get_property (GObject
* object
, guint propId
, GValue
*value
, GParamSpec
*pspec
)
2681 LOKDocView
* pDocView
= LOK_DOC_VIEW (object
);
2682 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2687 g_value_set_string (value
, priv
->m_aLOPath
.c_str());
2689 case PROP_LO_UNIPOLL
:
2690 g_value_set_boolean (value
, priv
->m_bUnipoll
);
2692 case PROP_LO_POINTER
:
2693 g_value_set_pointer(value
, priv
->m_pOffice
);
2695 case PROP_USER_PROFILE_URL
:
2696 g_value_set_string(value
, priv
->m_aUserProfileURL
.c_str());
2699 g_value_set_string (value
, priv
->m_aDocPath
.c_str());
2701 case PROP_DOC_POINTER
:
2702 g_value_set_pointer(value
, priv
->m_pDocument
);
2705 g_value_set_boolean (value
, priv
->m_bEdit
);
2707 case PROP_LOAD_PROGRESS
:
2708 g_value_set_double (value
, priv
->m_nLoadProgress
);
2711 g_value_set_float (value
, priv
->m_fZoom
);
2713 case PROP_IS_LOADING
:
2714 g_value_set_boolean (value
, priv
->m_bIsLoading
);
2716 case PROP_IS_INITIALIZED
:
2717 g_value_set_boolean (value
, priv
->m_bInit
);
2719 case PROP_DOC_WIDTH
:
2720 g_value_set_long (value
, priv
->m_nDocumentWidthTwips
);
2722 case PROP_DOC_HEIGHT
:
2723 g_value_set_long (value
, priv
->m_nDocumentHeightTwips
);
2725 case PROP_CAN_ZOOM_IN
:
2726 g_value_set_boolean (value
, priv
->m_bCanZoomIn
);
2728 case PROP_CAN_ZOOM_OUT
:
2729 g_value_set_boolean (value
, priv
->m_bCanZoomOut
);
2731 case PROP_DOC_PASSWORD
:
2732 g_value_set_boolean (value
, (priv
->m_nLOKFeatures
& LOK_FEATURE_DOCUMENT_PASSWORD
) != 0);
2734 case PROP_DOC_PASSWORD_TO_MODIFY
:
2735 g_value_set_boolean (value
, (priv
->m_nLOKFeatures
& LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY
) != 0);
2737 case PROP_TILED_ANNOTATIONS
:
2738 g_value_set_boolean (value
, !(priv
->m_nLOKFeatures
& LOK_FEATURE_NO_TILED_ANNOTATIONS
));
2741 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, propId
, pspec
);
2745 static gboolean
lok_doc_view_draw (GtkWidget
* pWidget
, cairo_t
* pCairo
)
2747 LOKDocView
*pDocView
= LOK_DOC_VIEW (pWidget
);
2749 renderDocument (pDocView
, pCairo
);
2750 renderOverlay (pDocView
, pCairo
);
2755 //rhbz#1444437 finalize may not occur immediately when this widget is destroyed
2756 //it may happen during GC of javascript, e.g. in gnome-documents but "destroy"
2757 //will be called promptly, so close documents in destroy, not finalize
2758 static void lok_doc_view_destroy (GtkWidget
* widget
)
2760 LOKDocView
* pDocView
= LOK_DOC_VIEW (widget
);
2761 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2763 // Ignore notifications sent to this view on shutdown.
2764 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2765 if (priv
->m_pDocument
)
2767 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
2768 priv
->m_pDocument
->pClass
->registerCallback(priv
->m_pDocument
, nullptr, nullptr);
2771 if (priv
->lokThreadPool
)
2773 g_thread_pool_free(priv
->lokThreadPool
, true, true);
2774 priv
->lokThreadPool
= nullptr;
2779 if (priv
->m_pDocument
)
2781 // This call may drop several views - e.g., embedded OLE in-place clients
2782 priv
->m_pDocument
->pClass
->destroyView(priv
->m_pDocument
, priv
->m_nViewId
);
2783 if (priv
->m_pDocument
->pClass
->getViewsCount(priv
->m_pDocument
) == 0)
2785 // Last view(s) gone
2786 priv
->m_pDocument
->pClass
->destroy (priv
->m_pDocument
);
2787 priv
->m_pDocument
= nullptr;
2788 if (priv
->m_pOffice
)
2790 priv
->m_pOffice
->pClass
->destroy (priv
->m_pOffice
);
2791 priv
->m_pOffice
= nullptr;
2796 GTK_WIDGET_CLASS (lok_doc_view_parent_class
)->destroy (widget
);
2799 static void lok_doc_view_finalize (GObject
* object
)
2801 LOKDocView
* pDocView
= LOK_DOC_VIEW (object
);
2802 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2804 delete priv
.m_pImpl
;
2805 priv
.m_pImpl
= nullptr;
2807 G_OBJECT_CLASS (lok_doc_view_parent_class
)->finalize (object
);
2810 // kicks the mainloop awake
2811 static gboolean
timeout_wakeup(void *)
2816 // integrate our mainloop with LOK's
2817 static int lok_poll_callback(void*, int timeoutUs
)
2819 bool bWasEvent(false);
2822 guint timeout
= g_timeout_add(timeoutUs
/ 1000, timeout_wakeup
, nullptr);
2823 bWasEvent
= g_main_context_iteration(nullptr, true);
2824 g_source_remove(timeout
);
2827 bWasEvent
= g_main_context_iteration(nullptr, timeoutUs
< 0);
2829 return bWasEvent
? 1 : 0;
2832 // thread-safe wakeup of our mainloop
2833 static void lok_wake_callback(void *)
2835 g_main_context_wakeup(nullptr);
2838 static gboolean
spin_lok_loop(void *pData
)
2840 LOKDocView
*pDocView
= LOK_DOC_VIEW (pData
);
2841 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2842 priv
->m_pOffice
->pClass
->runLoop(priv
->m_pOffice
, lok_poll_callback
, lok_wake_callback
, nullptr);
2846 // Update the client's view size
2847 static void updateClientZoom(LOKDocView
*pDocView
)
2849 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2851 return; // Not initialized yet?
2852 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
2853 gint nTileSizePixelsScaled
= nTileSizePixels
* nScaleFactor
;
2854 GError
* error
= nullptr;
2856 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
2857 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_CLIENT_ZOOM
);
2858 pLOEvent
->m_nTilePixelWidth
= nTileSizePixelsScaled
;
2859 pLOEvent
->m_nTilePixelHeight
= nTileSizePixelsScaled
;
2860 pLOEvent
->m_nTileTwipWidth
= pixelToTwip(nTileSizePixelsScaled
, priv
->m_fZoom
* nScaleFactor
);
2861 pLOEvent
->m_nTileTwipHeight
= pixelToTwip(nTileSizePixelsScaled
, priv
->m_fZoom
* nScaleFactor
);
2862 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
2864 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
2865 if (error
!= nullptr)
2867 g_warning("Unable to call LOK_SET_CLIENT_ZOOM: %s", error
->message
);
2868 g_clear_error(&error
);
2870 g_object_unref(task
);
2872 priv
->m_nTileSizeTwips
= pixelToTwip(nTileSizePixelsScaled
, priv
->m_fZoom
* nScaleFactor
);
2875 static gboolean
lok_doc_view_initable_init (GInitable
*initable
, GCancellable
* /*cancellable*/, GError
**error
)
2877 LOKDocView
*pDocView
= LOK_DOC_VIEW (initable
);
2878 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2880 if (priv
->m_pOffice
!= nullptr)
2883 if (priv
->m_bUnipoll
)
2884 (void)g_setenv("SAL_LOK_OPTIONS", "unipoll", FALSE
);
2886 static const char testingLangs
[] = "de_DE en_GB en_US es_ES fr_FR it nl pt_BR pt_PT ru";
2887 (void)g_setenv("LOK_ALLOWLIST_LANGUAGES", testingLangs
, FALSE
);
2889 priv
->m_pOffice
= lok_init_2(priv
->m_aLOPath
.c_str(), priv
->m_aUserProfileURL
.empty() ? nullptr : priv
->m_aUserProfileURL
.c_str());
2891 if (priv
->m_pOffice
== nullptr)
2894 g_quark_from_static_string ("LOK initialization error"), 0,
2895 "Failed to get LibreOfficeKit context. Make sure path (%s) is correct",
2896 priv
->m_aLOPath
.c_str());
2899 priv
->m_nLOKFeatures
|= LOK_FEATURE_PART_IN_INVALIDATION_CALLBACK
;
2900 priv
->m_nLOKFeatures
|= LOK_FEATURE_VIEWID_IN_VISCURSOR_INVALIDATION_CALLBACK
;
2901 priv
->m_pOffice
->pClass
->setOptionalFeatures(priv
->m_pOffice
, priv
->m_nLOKFeatures
);
2903 if (priv
->m_bUnipoll
)
2904 g_idle_add(spin_lok_loop
, pDocView
);
2909 static void lok_doc_view_initable_iface_init (GInitableIface
*iface
)
2911 iface
->init
= lok_doc_view_initable_init
;
2914 static void lok_doc_view_class_init (LOKDocViewClass
* pClass
)
2916 GObjectClass
*pGObjectClass
= G_OBJECT_CLASS(pClass
);
2917 GtkWidgetClass
*pWidgetClass
= GTK_WIDGET_CLASS(pClass
);
2919 pGObjectClass
->get_property
= lok_doc_view_get_property
;
2920 pGObjectClass
->set_property
= lok_doc_view_set_property
;
2921 pGObjectClass
->finalize
= lok_doc_view_finalize
;
2923 pWidgetClass
->draw
= lok_doc_view_draw
;
2924 pWidgetClass
->button_press_event
= lok_doc_view_signal_button
;
2925 pWidgetClass
->button_release_event
= lok_doc_view_signal_button
;
2926 pWidgetClass
->key_press_event
= signalKey
;
2927 pWidgetClass
->key_release_event
= signalKey
;
2928 pWidgetClass
->motion_notify_event
= lok_doc_view_signal_motion
;
2929 pWidgetClass
->destroy
= lok_doc_view_destroy
;
2932 * LOKDocView:lopath:
2934 * The absolute path of the LibreOffice install.
2936 properties
[PROP_LO_PATH
] =
2937 g_param_spec_string("lopath",
2939 "LibreOffice Install Path",
2941 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2942 G_PARAM_CONSTRUCT_ONLY
|
2943 G_PARAM_STATIC_STRINGS
));
2946 * LOKDocView:unipoll:
2948 * Whether we use our own unified polling mainloop in place of glib's
2950 properties
[PROP_LO_UNIPOLL
] =
2951 g_param_spec_boolean("unipoll",
2953 "Whether we use a custom unified polling loop",
2955 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2956 G_PARAM_CONSTRUCT_ONLY
|
2957 G_PARAM_STATIC_STRINGS
));
2959 * LOKDocView:lopointer:
2961 * A LibreOfficeKit* in case lok_init() is already called
2964 properties
[PROP_LO_POINTER
] =
2965 g_param_spec_pointer("lopointer",
2967 "A LibreOfficeKit* from lok_init()",
2968 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2969 G_PARAM_CONSTRUCT_ONLY
|
2970 G_PARAM_STATIC_STRINGS
));
2973 * LOKDocView:userprofileurl:
2975 * The absolute path of the LibreOffice user profile.
2977 properties
[PROP_USER_PROFILE_URL
] =
2978 g_param_spec_string("userprofileurl",
2979 "User profile path",
2980 "LibreOffice user profile path",
2982 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2983 G_PARAM_CONSTRUCT_ONLY
|
2984 G_PARAM_STATIC_STRINGS
));
2987 * LOKDocView:docpath:
2989 * The path of the document that is currently being viewed.
2991 properties
[PROP_DOC_PATH
] =
2992 g_param_spec_string("docpath",
2994 "The URI of the document to open",
2996 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2997 G_PARAM_STATIC_STRINGS
));
3000 * LOKDocView:docpointer:
3002 * A LibreOfficeKitDocument* in case documentLoad() is already called
3005 properties
[PROP_DOC_POINTER
] =
3006 g_param_spec_pointer("docpointer",
3008 "A LibreOfficeKitDocument* from documentLoad()",
3009 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
3010 G_PARAM_STATIC_STRINGS
));
3013 * LOKDocView:editable:
3015 * Whether the document loaded inside of #LOKDocView is editable or not.
3017 properties
[PROP_EDITABLE
] =
3018 g_param_spec_boolean("editable",
3020 "Whether the content is in edit mode or not",
3022 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
3023 G_PARAM_STATIC_STRINGS
));
3026 * LOKDocView:load-progress:
3028 * The percent completion of the current loading operation of the
3029 * document. This can be used for progress bars. Note that this is not a
3030 * very accurate progress indicator, and its value might reset it couple of
3031 * times to 0 and start again. You should not rely on its numbers.
3033 properties
[PROP_LOAD_PROGRESS
] =
3034 g_param_spec_double("load-progress",
3035 "Estimated Load Progress",
3036 "Shows the progress of the document load operation",
3038 static_cast<GParamFlags
>(G_PARAM_READABLE
|
3039 G_PARAM_STATIC_STRINGS
));
3042 * LOKDocView:zoom-level:
3044 * The current zoom level of the document loaded inside #LOKDocView. The
3045 * default value is 1.0.
3047 properties
[PROP_ZOOM
] =
3048 g_param_spec_float("zoom-level",
3050 "The current zoom level of the content",
3052 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
3053 G_PARAM_STATIC_STRINGS
));
3056 * LOKDocView:is-loading:
3058 * Whether the requested document is being loaded or not. %TRUE if it is
3059 * being loaded, otherwise %FALSE.
3061 properties
[PROP_IS_LOADING
] =
3062 g_param_spec_boolean("is-loading",
3064 "Whether the view is loading a document",
3066 static_cast<GParamFlags
>(G_PARAM_READABLE
|
3067 G_PARAM_STATIC_STRINGS
));
3070 * LOKDocView:is-initialized:
3072 * Whether the requested document has completely loaded or not.
3074 properties
[PROP_IS_INITIALIZED
] =
3075 g_param_spec_boolean("is-initialized",
3077 "Whether the view has completely initialized",
3079 static_cast<GParamFlags
>(G_PARAM_READABLE
|
3080 G_PARAM_STATIC_STRINGS
));
3083 * LOKDocView:doc-width:
3085 * The width of the currently loaded document in #LOKDocView in twips.
3087 properties
[PROP_DOC_WIDTH
] =
3088 g_param_spec_long("doc-width",
3090 "Width of the document in twips",
3092 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
3093 G_PARAM_STATIC_STRINGS
));
3096 * LOKDocView:doc-height:
3098 * The height of the currently loaded document in #LOKDocView in twips.
3100 properties
[PROP_DOC_HEIGHT
] =
3101 g_param_spec_long("doc-height",
3103 "Height of the document in twips",
3105 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
3106 G_PARAM_STATIC_STRINGS
));
3109 * LOKDocView:can-zoom-in:
3111 * It tells whether the view can further be zoomed in or not.
3113 properties
[PROP_CAN_ZOOM_IN
] =
3114 g_param_spec_boolean("can-zoom-in",
3116 "Whether the view can be zoomed in further",
3118 static_cast<GParamFlags
>(G_PARAM_READABLE
3119 | G_PARAM_STATIC_STRINGS
));
3122 * LOKDocView:can-zoom-out:
3124 * It tells whether the view can further be zoomed out or not.
3126 properties
[PROP_CAN_ZOOM_OUT
] =
3127 g_param_spec_boolean("can-zoom-out",
3129 "Whether the view can be zoomed out further",
3131 static_cast<GParamFlags
>(G_PARAM_READABLE
3132 | G_PARAM_STATIC_STRINGS
));
3135 * LOKDocView:doc-password:
3137 * Set it to true if client supports providing password for viewing
3138 * password protected documents
3140 properties
[PROP_DOC_PASSWORD
] =
3141 g_param_spec_boolean("doc-password",
3142 "Document password capability",
3143 "Whether client supports providing document passwords",
3145 static_cast<GParamFlags
>(G_PARAM_READWRITE
3146 | G_PARAM_STATIC_STRINGS
));
3149 * LOKDocView:doc-password-to-modify:
3151 * Set it to true if client supports providing password for edit-protected documents
3153 properties
[PROP_DOC_PASSWORD_TO_MODIFY
] =
3154 g_param_spec_boolean("doc-password-to-modify",
3155 "Edit document password capability",
3156 "Whether the client supports providing passwords to edit documents",
3158 static_cast<GParamFlags
>(G_PARAM_READWRITE
3159 | G_PARAM_STATIC_STRINGS
));
3162 * LOKDocView:tiled-annotations-rendering:
3164 * Set it to false if client does not want LO to render comments in tiles and
3165 * instead interested in using comments API to access comments
3167 properties
[PROP_TILED_ANNOTATIONS
] =
3168 g_param_spec_boolean("tiled-annotations",
3169 "Render comments in tiles",
3170 "Whether the client wants in tile comment rendering",
3172 static_cast<GParamFlags
>(G_PARAM_READWRITE
3173 | G_PARAM_STATIC_STRINGS
));
3175 g_object_class_install_properties(pGObjectClass
, PROP_LAST
, properties
);
3178 * LOKDocView::load-changed:
3179 * @pDocView: the #LOKDocView on which the signal is emitted
3180 * @fLoadProgress: the new progress value
3182 doc_view_signals
[LOAD_CHANGED
] =
3183 g_signal_new("load-changed",
3184 G_TYPE_FROM_CLASS (pGObjectClass
),
3188 g_cclosure_marshal_VOID__DOUBLE
,
3193 * LOKDocView::edit-changed:
3194 * @pDocView: the #LOKDocView on which the signal is emitted
3195 * @bEdit: the new edit value of the view
3197 doc_view_signals
[EDIT_CHANGED
] =
3198 g_signal_new("edit-changed",
3199 G_TYPE_FROM_CLASS (pGObjectClass
),
3203 g_cclosure_marshal_VOID__BOOLEAN
,
3208 * LOKDocView::command-changed:
3209 * @pDocView: the #LOKDocView on which the signal is emitted
3210 * @aCommand: the command that was changed
3212 doc_view_signals
[COMMAND_CHANGED
] =
3213 g_signal_new("command-changed",
3214 G_TYPE_FROM_CLASS(pGObjectClass
),
3218 g_cclosure_marshal_VOID__STRING
,
3223 * LOKDocView::search-not-found:
3224 * @pDocView: the #LOKDocView on which the signal is emitted
3225 * @aCommand: the string for which the search was not found.
3227 doc_view_signals
[SEARCH_NOT_FOUND
] =
3228 g_signal_new("search-not-found",
3229 G_TYPE_FROM_CLASS(pGObjectClass
),
3233 g_cclosure_marshal_VOID__STRING
,
3238 * LOKDocView::part-changed:
3239 * @pDocView: the #LOKDocView on which the signal is emitted
3240 * @aCommand: the part number which the view changed to
3242 doc_view_signals
[PART_CHANGED
] =
3243 g_signal_new("part-changed",
3244 G_TYPE_FROM_CLASS(pGObjectClass
),
3248 g_cclosure_marshal_VOID__INT
,
3253 * LOKDocView::size-changed:
3254 * @pDocView: the #LOKDocView on which the signal is emitted
3255 * @aCommand: NULL, we just notify that want to notify the UI elements that are interested.
3257 doc_view_signals
[SIZE_CHANGED
] =
3258 g_signal_new("size-changed",
3259 G_TYPE_FROM_CLASS(pGObjectClass
),
3263 g_cclosure_marshal_VOID__VOID
,
3268 * LOKDocView::hyperlinked-clicked:
3269 * @pDocView: the #LOKDocView on which the signal is emitted
3270 * @aHyperlink: the URI which the application should handle
3272 doc_view_signals
[HYPERLINK_CLICKED
] =
3273 g_signal_new("hyperlink-clicked",
3274 G_TYPE_FROM_CLASS(pGObjectClass
),
3278 g_cclosure_marshal_VOID__STRING
,
3283 * LOKDocView::cursor-changed:
3284 * @pDocView: the #LOKDocView on which the signal is emitted
3285 * @nX: The new cursor position (X coordinate) in pixels
3286 * @nY: The new cursor position (Y coordinate) in pixels
3287 * @nWidth: The width of new cursor
3288 * @nHeight: The height of new cursor
3290 doc_view_signals
[CURSOR_CHANGED
] =
3291 g_signal_new("cursor-changed",
3292 G_TYPE_FROM_CLASS(pGObjectClass
),
3296 g_cclosure_marshal_generic
,
3298 G_TYPE_INT
, G_TYPE_INT
,
3299 G_TYPE_INT
, G_TYPE_INT
);
3302 * LOKDocView::search-result-count:
3303 * @pDocView: the #LOKDocView on which the signal is emitted
3304 * @aCommand: number of matches.
3306 doc_view_signals
[SEARCH_RESULT_COUNT
] =
3307 g_signal_new("search-result-count",
3308 G_TYPE_FROM_CLASS(pGObjectClass
),
3312 g_cclosure_marshal_VOID__STRING
,
3317 * LOKDocView::command-result:
3318 * @pDocView: the #LOKDocView on which the signal is emitted
3319 * @aCommand: JSON containing the info about the command that finished,
3320 * and its success status.
3322 doc_view_signals
[COMMAND_RESULT
] =
3323 g_signal_new("command-result",
3324 G_TYPE_FROM_CLASS(pGObjectClass
),
3328 g_cclosure_marshal_VOID__STRING
,
3333 * LOKDocView::address-changed:
3334 * @pDocView: the #LOKDocView on which the signal is emitted
3335 * @aCommand: formula text content
3337 doc_view_signals
[ADDRESS_CHANGED
] =
3338 g_signal_new("address-changed",
3339 G_TYPE_FROM_CLASS(pGObjectClass
),
3343 g_cclosure_marshal_VOID__STRING
,
3348 * LOKDocView::formula-changed:
3349 * @pDocView: the #LOKDocView on which the signal is emitted
3350 * @aCommand: formula text content
3352 doc_view_signals
[FORMULA_CHANGED
] =
3353 g_signal_new("formula-changed",
3354 G_TYPE_FROM_CLASS(pGObjectClass
),
3358 g_cclosure_marshal_VOID__STRING
,
3363 * LOKDocView::text-selection:
3364 * @pDocView: the #LOKDocView on which the signal is emitted
3365 * @bIsTextSelected: whether text selected is non-null
3367 doc_view_signals
[TEXT_SELECTION
] =
3368 g_signal_new("text-selection",
3369 G_TYPE_FROM_CLASS(pGObjectClass
),
3373 g_cclosure_marshal_VOID__BOOLEAN
,
3378 * LOKDocView::content-control:
3379 * @pDocView: the #LOKDocView on which the signal is emitted
3380 * @pPayload: the JSON string containing the information about ruler properties
3382 doc_view_signals
[CONTENT_CONTROL
] =
3383 g_signal_new("content-control",
3384 G_TYPE_FROM_CLASS(pGObjectClass
),
3388 g_cclosure_marshal_generic
,
3393 * LOKDocView::password-required:
3394 * @pDocView: the #LOKDocView on which the signal is emitted
3395 * @pUrl: URL of the document for which password is required
3396 * @bModify: whether password id required to modify the document
3397 * This is true when password is required to edit the document,
3398 * while it can still be viewed without password. In such cases, provide a NULL
3399 * password for read-only access to the document.
3400 * If false, password is required for opening the document, and document
3401 * cannot be opened without providing a valid password.
3403 * Password must be provided by calling lok_doc_view_set_document_password
3404 * function with pUrl as provided by the callback.
3406 * Upon entering an invalid password, another `password-required` signal is
3408 * Upon entering a valid password, document starts to load.
3409 * Upon entering a NULL password: if bModify is %TRUE, document starts to
3410 * open in view-only mode, else loading of document is aborted.
3412 doc_view_signals
[PASSWORD_REQUIRED
] =
3413 g_signal_new("password-required",
3414 G_TYPE_FROM_CLASS(pGObjectClass
),
3418 g_cclosure_marshal_generic
,
3424 * LOKDocView::comment:
3425 * @pDocView: the #LOKDocView on which the signal is emitted
3426 * @pComment: the JSON string containing comment notification
3427 * The has following structure containing the information telling whether
3428 * the comment has been added, deleted or modified.
3435 * "author": "Unknown Author",
3436 * "text": "This is a comment",
3437 * "dateTime": "2016-08-18T13:13:00",
3438 * "anchorPos": "4529, 3906",
3439 * "textRange": "1418, 3906, 3111, 919"
3442 * 'action' can be 'Add', 'Remove' or 'Modify' depending on whether
3443 * comment has been added, removed or modified.
3444 * 'parent' is a non-zero comment id if this comment is a reply comment,
3445 * otherwise it's a root comment.
3447 doc_view_signals
[COMMENT
] =
3448 g_signal_new("comment",
3449 G_TYPE_FROM_CLASS(pGObjectClass
),
3453 g_cclosure_marshal_generic
,
3458 * LOKDocView::ruler:
3459 * @pDocView: the #LOKDocView on which the signal is emitted
3460 * @pPayload: the JSON string containing the information about ruler properties
3462 * The payload format is:
3467 * "leftOffset": "...",
3468 * "pageOffset": "...",
3469 * "pageWidth": "...",
3473 doc_view_signals
[RULER
] =
3474 g_signal_new("ruler",
3475 G_TYPE_FROM_CLASS(pGObjectClass
),
3479 g_cclosure_marshal_generic
,
3484 * LOKDocView::window::
3485 * @pDocView: the #LOKDocView on which the signal is emitted
3486 * @pPayload: the JSON string containing the information about the window
3488 * This signal emits information about external windows like dialogs, autopopups for now.
3490 * The payload format of pPayload is:
3493 * "id": "unique integer id of the dialog",
3494 * "action": "<see below>",
3495 * "type": "<see below>"
3496 * "rectangle": "x, y, width, height"
3499 * "type" tells the type of the window the action is associated with
3500 * - "dialog" - window is a dialog
3501 * - "child" - window is a floating window (combo boxes, etc.)
3503 * "action" can take following values:
3504 * - "created" - window is created in the backend, client can render it now
3505 * - "title_changed" - window's title is changed
3506 * - "size_changed" - window's size is changed
3507 * - "invalidate" - the area as described by "rectangle" is invalidated
3508 * Clients must request the new area
3509 * - "cursor_invalidate" - cursor is invalidated. New position is in "rectangle"
3510 * - "cursor_visible" - cursor visible status is changed. Status is available
3511 * in "visible" field
3512 * - "close" - window is closed
3514 doc_view_signals
[WINDOW
] =
3515 g_signal_new("window",
3516 G_TYPE_FROM_CLASS(pGObjectClass
),
3520 g_cclosure_marshal_generic
,
3525 * LOKDocView::invalidate-header::
3526 * @pDocView: the #LOKDocView on which the signal is emitted
3527 * @pPayload: can be either "row", "column", or "all".
3529 * The column/row header is no more valid because of a column/row insertion
3530 * or a similar event. Clients must query a new column/row header set.
3532 * The payload says if we are invalidating a row or column header
3534 doc_view_signals
[INVALIDATE_HEADER
] =
3535 g_signal_new("invalidate-header",
3536 G_TYPE_FROM_CLASS(pGObjectClass
),
3540 g_cclosure_marshal_generic
,
3545 SAL_DLLPUBLIC_EXPORT GtkWidget
*
3546 lok_doc_view_new (const gchar
* pPath
, GCancellable
*cancellable
, GError
**error
)
3548 return GTK_WIDGET (g_initable_new (LOK_TYPE_DOC_VIEW
, cancellable
, error
,
3549 "lopath", pPath
== nullptr ? LOK_PATH
: pPath
,
3550 "halign", GTK_ALIGN_CENTER
,
3551 "valign", GTK_ALIGN_CENTER
,
3555 SAL_DLLPUBLIC_EXPORT GtkWidget
*
3556 lok_doc_view_new_from_user_profile (const gchar
* pPath
, const gchar
* pUserProfile
, GCancellable
*cancellable
, GError
**error
)
3558 return GTK_WIDGET(g_initable_new(LOK_TYPE_DOC_VIEW
, cancellable
, error
,
3559 "lopath", pPath
== nullptr ? LOK_PATH
: pPath
,
3560 "userprofileurl", pUserProfile
,
3561 "halign", GTK_ALIGN_CENTER
,
3562 "valign", GTK_ALIGN_CENTER
,
3566 SAL_DLLPUBLIC_EXPORT GtkWidget
* lok_doc_view_new_from_widget(LOKDocView
* pOldLOKDocView
,
3567 const gchar
* pRenderingArguments
)
3569 LOKDocViewPrivate
& pOldPriv
= getPrivate(pOldLOKDocView
);
3570 GtkWidget
* pNewDocView
= GTK_WIDGET(g_initable_new(LOK_TYPE_DOC_VIEW
, /*cancellable=*/nullptr, /*error=*/nullptr,
3571 "lopath", pOldPriv
->m_aLOPath
.c_str(),
3572 "userprofileurl", pOldPriv
->m_aUserProfileURL
.c_str(),
3573 "lopointer", pOldPriv
->m_pOffice
,
3574 "docpointer", pOldPriv
->m_pDocument
,
3575 "halign", GTK_ALIGN_CENTER
,
3576 "valign", GTK_ALIGN_CENTER
,
3579 // No documentLoad(), just a createView().
3580 LibreOfficeKitDocument
* pDocument
= lok_doc_view_get_document(LOK_DOC_VIEW(pNewDocView
));
3581 LOKDocViewPrivate
& pNewPriv
= getPrivate(LOK_DOC_VIEW(pNewDocView
));
3582 // Store the view id only later in postDocumentLoad(), as
3583 // initializeForRendering() changes the id in Impress.
3584 pDocument
->pClass
->createView(pDocument
);
3585 pNewPriv
->m_aRenderingArguments
= pRenderingArguments
;
3587 postDocumentLoad(pNewDocView
);
3591 SAL_DLLPUBLIC_EXPORT gboolean
3592 lok_doc_view_open_document_finish (LOKDocView
* pDocView
, GAsyncResult
* res
, GError
** error
)
3594 GTask
* task
= G_TASK(res
);
3596 g_return_val_if_fail(g_task_is_valid(res
, pDocView
), false);
3597 g_return_val_if_fail(g_task_get_source_tag(task
) == lok_doc_view_open_document
, false);
3598 g_return_val_if_fail(error
== nullptr || *error
== nullptr, false);
3600 return g_task_propagate_boolean(task
, error
);
3603 SAL_DLLPUBLIC_EXPORT
void
3604 lok_doc_view_open_document (LOKDocView
* pDocView
,
3606 const gchar
* pRenderingArguments
,
3607 GCancellable
* cancellable
,
3608 GAsyncReadyCallback callback
,
3611 GTask
* task
= g_task_new(pDocView
, cancellable
, callback
, userdata
);
3612 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3613 GError
* error
= nullptr;
3615 LOEvent
* pLOEvent
= new LOEvent(LOK_LOAD_DOC
);
3617 g_object_set(G_OBJECT(pDocView
), "docpath", pPath
, nullptr);
3618 if (pRenderingArguments
)
3619 priv
->m_aRenderingArguments
= pRenderingArguments
;
3620 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
3621 g_task_set_source_tag(task
, reinterpret_cast<gpointer
>(lok_doc_view_open_document
));
3623 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
3624 if (error
!= nullptr)
3626 g_warning("Unable to call LOK_LOAD_DOC: %s", error
->message
);
3627 g_clear_error(&error
);
3629 g_object_unref(task
);
3632 SAL_DLLPUBLIC_EXPORT LibreOfficeKitDocument
*
3633 lok_doc_view_get_document (LOKDocView
* pDocView
)
3635 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3636 return priv
->m_pDocument
;
3639 SAL_DLLPUBLIC_EXPORT
void
3640 lok_doc_view_set_visible_area (LOKDocView
* pDocView
, GdkRectangle
* pVisibleArea
)
3645 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3646 priv
->m_aVisibleArea
= *pVisibleArea
;
3647 priv
->m_bVisibleAreaSet
= true;
3651 // This used to be rtl::math::approxEqual() but since that isn't inline anymore
3652 // in rtl/math.hxx and was moved into libuno_sal as rtl_math_approxEqual() to
3653 // cater for representable integer cases and we don't want to link against
3654 // libuno_sal, we'll have to have an own implementation. The special large
3655 // integer cases seems not be needed here.
3656 bool lok_approxEqual(double a
, double b
)
3658 static const double e48
= 1.0 / (16777216.0 * 16777216.0);
3661 if (a
== 0.0 || b
== 0.0)
3663 const double d
= fabs(a
- b
);
3664 return (d
< fabs(a
) * e48
&& d
< fabs(b
) * e48
);
3668 SAL_DLLPUBLIC_EXPORT
void
3669 lok_doc_view_set_zoom (LOKDocView
* pDocView
, float fZoom
)
3671 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3673 if (!priv
->m_pDocument
)
3676 // Clamp the input value in [MIN_ZOOM, MAX_ZOOM]
3677 fZoom
= fZoom
< MIN_ZOOM
? MIN_ZOOM
: fZoom
;
3678 fZoom
= std::min(fZoom
, MAX_ZOOM
);
3680 if (lok_approxEqual(fZoom
, priv
->m_fZoom
))
3683 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
3684 gint nTileSizePixelsScaled
= nTileSizePixels
* nScaleFactor
;
3685 priv
->m_fZoom
= fZoom
;
3686 long nDocumentWidthPixels
= twipToPixel(priv
->m_nDocumentWidthTwips
, fZoom
* nScaleFactor
);
3687 long nDocumentHeightPixels
= twipToPixel(priv
->m_nDocumentHeightTwips
, fZoom
* nScaleFactor
);
3688 // Total number of columns in this document.
3689 guint nColumns
= ceil(static_cast<double>(nDocumentWidthPixels
) / nTileSizePixelsScaled
);
3690 priv
->m_pTileBuffer
= std::make_unique
<TileBuffer
>(nColumns
, nScaleFactor
);
3691 gtk_widget_set_size_request(GTK_WIDGET(pDocView
),
3692 nDocumentWidthPixels
/ nScaleFactor
,
3693 nDocumentHeightPixels
/ nScaleFactor
);
3695 g_object_notify_by_pspec(G_OBJECT(pDocView
), properties
[PROP_ZOOM
]);
3697 // set properties to indicate if view can be further zoomed in/out
3698 bool bCanZoomIn
= priv
->m_fZoom
< MAX_ZOOM
;
3699 bool bCanZoomOut
= priv
->m_fZoom
> MIN_ZOOM
;
3700 if (bCanZoomIn
!= bool(priv
->m_bCanZoomIn
))
3702 priv
->m_bCanZoomIn
= bCanZoomIn
;
3703 g_object_notify_by_pspec(G_OBJECT(pDocView
), properties
[PROP_CAN_ZOOM_IN
]);
3705 if (bCanZoomOut
!= bool(priv
->m_bCanZoomOut
))
3707 priv
->m_bCanZoomOut
= bCanZoomOut
;
3708 g_object_notify_by_pspec(G_OBJECT(pDocView
), properties
[PROP_CAN_ZOOM_OUT
]);
3711 updateClientZoom(pDocView
);
3714 SAL_DLLPUBLIC_EXPORT gfloat
3715 lok_doc_view_get_zoom (LOKDocView
* pDocView
)
3717 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3718 return priv
->m_fZoom
;
3721 SAL_DLLPUBLIC_EXPORT gint
3722 lok_doc_view_get_parts (LOKDocView
* pDocView
)
3724 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3725 if (!priv
->m_pDocument
)
3728 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
3729 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
3730 return priv
->m_pDocument
->pClass
->getParts( priv
->m_pDocument
);
3733 SAL_DLLPUBLIC_EXPORT gint
3734 lok_doc_view_get_part (LOKDocView
* pDocView
)
3736 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3737 if (!priv
->m_pDocument
)
3740 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
3741 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
3742 return priv
->m_pDocument
->pClass
->getPart( priv
->m_pDocument
);
3745 SAL_DLLPUBLIC_EXPORT
void
3746 lok_doc_view_set_part (LOKDocView
* pDocView
, int nPart
)
3748 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3749 if (!priv
->m_pDocument
)
3752 if (nPart
< 0 || nPart
>= priv
->m_nParts
)
3754 g_warning("Invalid part request : %d", nPart
);
3758 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
3759 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_PART
);
3760 GError
* error
= nullptr;
3762 pLOEvent
->m_nPart
= nPart
;
3763 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
3765 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
3766 if (error
!= nullptr)
3768 g_warning("Unable to call LOK_SET_PART: %s", error
->message
);
3769 g_clear_error(&error
);
3771 g_object_unref(task
);
3772 priv
->m_nPartId
= nPart
;
3775 SAL_DLLPUBLIC_EXPORT
void lok_doc_view_send_content_control_event(LOKDocView
* pDocView
,
3776 const gchar
* pArguments
)
3778 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3779 if (!priv
->m_pDocument
)
3784 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
3785 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
3786 return priv
->m_pDocument
->pClass
->sendContentControlEvent(priv
->m_pDocument
, pArguments
);
3789 SAL_DLLPUBLIC_EXPORT gchar
*
3790 lok_doc_view_get_part_name (LOKDocView
* pDocView
, int nPart
)
3792 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3793 if (!priv
->m_pDocument
)
3796 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
3797 setDocumentView(priv
->m_pDocument
, priv
->m_nViewId
);
3798 return priv
->m_pDocument
->pClass
->getPartName( priv
->m_pDocument
, nPart
);
3801 SAL_DLLPUBLIC_EXPORT
void
3802 lok_doc_view_set_partmode(LOKDocView
* pDocView
,
3805 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3806 if (!priv
->m_pDocument
)
3809 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
3810 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_PARTMODE
);
3811 GError
* error
= nullptr;
3813 pLOEvent
->m_nPartMode
= nPartMode
;
3814 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
3816 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
3817 if (error
!= nullptr)
3819 g_warning("Unable to call LOK_SET_PARTMODE: %s", error
->message
);
3820 g_clear_error(&error
);
3822 g_object_unref(task
);
3825 SAL_DLLPUBLIC_EXPORT
void
3826 lok_doc_view_reset_view(LOKDocView
* pDocView
)
3828 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3830 if (priv
->m_pTileBuffer
!= nullptr)
3831 priv
->m_pTileBuffer
->resetAllTiles();
3832 priv
->m_nLoadProgress
= 0.0;
3834 memset(&priv
->m_aVisibleCursor
, 0, sizeof(priv
->m_aVisibleCursor
));
3835 priv
->m_bCursorOverlayVisible
= false;
3836 priv
->m_bCursorVisible
= false;
3838 priv
->m_nLastButtonPressTime
= 0;
3839 priv
->m_nLastButtonReleaseTime
= 0;
3840 priv
->m_aTextSelectionRectangles
.clear();
3841 priv
->m_aContentControlRectangles
.clear();
3843 memset(&priv
->m_aTextSelectionStart
, 0, sizeof(priv
->m_aTextSelectionStart
));
3844 memset(&priv
->m_aTextSelectionEnd
, 0, sizeof(priv
->m_aTextSelectionEnd
));
3845 memset(&priv
->m_aGraphicSelection
, 0, sizeof(priv
->m_aGraphicSelection
));
3846 priv
->m_bInDragGraphicSelection
= false;
3847 memset(&priv
->m_aCellCursor
, 0, sizeof(priv
->m_aCellCursor
));
3849 cairo_surface_destroy(priv
->m_pHandleStart
);
3850 priv
->m_pHandleStart
= nullptr;
3851 memset(&priv
->m_aHandleStartRect
, 0, sizeof(priv
->m_aHandleStartRect
));
3852 priv
->m_bInDragStartHandle
= false;
3854 cairo_surface_destroy(priv
->m_pHandleMiddle
);
3855 priv
->m_pHandleMiddle
= nullptr;
3856 memset(&priv
->m_aHandleMiddleRect
, 0, sizeof(priv
->m_aHandleMiddleRect
));
3857 priv
->m_bInDragMiddleHandle
= false;
3859 cairo_surface_destroy(priv
->m_pHandleEnd
);
3860 priv
->m_pHandleEnd
= nullptr;
3861 memset(&priv
->m_aHandleEndRect
, 0, sizeof(priv
->m_aHandleEndRect
));
3862 priv
->m_bInDragEndHandle
= false;
3864 memset(&priv
->m_aGraphicHandleRects
, 0, sizeof(priv
->m_aGraphicHandleRects
));
3865 memset(&priv
->m_bInDragGraphicHandles
, 0, sizeof(priv
->m_bInDragGraphicHandles
));
3867 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
3870 SAL_DLLPUBLIC_EXPORT
void
3871 lok_doc_view_set_edit(LOKDocView
* pDocView
,
3874 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3875 if (!priv
->m_pDocument
)
3878 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
3879 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_EDIT
);
3880 GError
* error
= nullptr;
3882 pLOEvent
->m_bEdit
= bEdit
;
3883 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
3885 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
3886 if (error
!= nullptr)
3888 g_warning("Unable to call LOK_SET_EDIT: %s", error
->message
);
3889 g_clear_error(&error
);
3891 g_object_unref(task
);
3894 SAL_DLLPUBLIC_EXPORT gboolean
3895 lok_doc_view_get_edit (LOKDocView
* pDocView
)
3897 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3898 return priv
->m_bEdit
;
3901 SAL_DLLPUBLIC_EXPORT
void
3902 lok_doc_view_post_command (LOKDocView
* pDocView
,
3903 const gchar
* pCommand
,
3904 const gchar
* pArguments
,
3905 gboolean bNotifyWhenFinished
)
3907 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3908 if (!priv
->m_pDocument
)
3912 LOKPostCommand(pDocView
, pCommand
, pArguments
, bNotifyWhenFinished
);
3914 g_info ("LOK_POST_COMMAND: ignoring commands in view-only mode");
3917 SAL_DLLPUBLIC_EXPORT gchar
*
3918 lok_doc_view_get_command_values (LOKDocView
* pDocView
,
3919 const gchar
* pCommand
)
3921 g_return_val_if_fail (LOK_IS_DOC_VIEW (pDocView
), nullptr);
3922 g_return_val_if_fail (pCommand
!= nullptr, nullptr);
3924 LibreOfficeKitDocument
* pDocument
= lok_doc_view_get_document(pDocView
);
3928 return pDocument
->pClass
->getCommandValues(pDocument
, pCommand
);
3931 SAL_DLLPUBLIC_EXPORT
void
3932 lok_doc_view_find_prev (LOKDocView
* pDocView
,
3934 gboolean bHighlightAll
)
3936 doSearch(pDocView
, pText
, true, bHighlightAll
);
3939 SAL_DLLPUBLIC_EXPORT
void
3940 lok_doc_view_find_next (LOKDocView
* pDocView
,
3942 gboolean bHighlightAll
)
3944 doSearch(pDocView
, pText
, false, bHighlightAll
);
3947 SAL_DLLPUBLIC_EXPORT
void
3948 lok_doc_view_highlight_all (LOKDocView
* pDocView
,
3951 doSearch(pDocView
, pText
, false, true);
3954 SAL_DLLPUBLIC_EXPORT gchar
*
3955 lok_doc_view_copy_selection (LOKDocView
* pDocView
,
3956 const gchar
* pMimeType
,
3957 gchar
** pUsedMimeType
)
3959 LibreOfficeKitDocument
* pDocument
= lok_doc_view_get_document(pDocView
);
3963 std::stringstream ss
;
3964 ss
<< "lok::Document::getTextSelection('" << pMimeType
<< "')";
3965 g_info("%s", ss
.str().c_str());
3966 return pDocument
->pClass
->getTextSelection(pDocument
, pMimeType
, pUsedMimeType
);
3969 SAL_DLLPUBLIC_EXPORT gboolean
3970 lok_doc_view_paste (LOKDocView
* pDocView
,
3971 const gchar
* pMimeType
,
3975 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3976 LibreOfficeKitDocument
* pDocument
= priv
->m_pDocument
;
3984 g_info ("ignoring paste in view-only mode");
3990 std::stringstream ss
;
3991 ss
<< "lok::Document::paste('" << pMimeType
<< "', '" << std::string(pData
, nSize
) << ", "<<nSize
<<"')";
3992 g_info("%s", ss
.str().c_str());
3993 ret
= pDocument
->pClass
->paste(pDocument
, pMimeType
, pData
, nSize
);
3999 SAL_DLLPUBLIC_EXPORT
void
4000 lok_doc_view_set_document_password (LOKDocView
* pDocView
,
4002 const gchar
* pPassword
)
4004 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
4006 priv
->m_pOffice
->pClass
->setDocumentPassword(priv
->m_pOffice
, pURL
, pPassword
);
4009 SAL_DLLPUBLIC_EXPORT gchar
*
4010 lok_doc_view_get_version_info (LOKDocView
* pDocView
)
4012 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
4014 return priv
->m_pOffice
->pClass
->getVersionInfo(priv
->m_pOffice
);
4018 SAL_DLLPUBLIC_EXPORT gfloat
4019 lok_doc_view_pixel_to_twip (LOKDocView
* pDocView
, float fInput
)
4021 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
4022 return pixelToTwip(fInput
, priv
->m_fZoom
);
4025 SAL_DLLPUBLIC_EXPORT gfloat
4026 lok_doc_view_twip_to_pixel (LOKDocView
* pDocView
, float fInput
)
4028 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
4029 return twipToPixel(fInput
, priv
->m_fZoom
);
4032 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */