1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <sal/types.h>
18 #include <boost/property_tree/json_parser.hpp>
20 #include <com/sun/star/awt/Key.hpp>
21 #include <LibreOfficeKit/LibreOfficeKit.h>
22 #include <LibreOfficeKit/LibreOfficeKitInit.h>
23 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
24 #include <LibreOfficeKit/LibreOfficeKitGtk.h>
25 #include <vcl/event.hxx>
27 #include "tilebuffer.hxx"
29 #if !GLIB_CHECK_VERSION(2,32,0)
30 #define G_SOURCE_REMOVE FALSE
31 #define G_SOURCE_CONTINUE TRUE
33 #if !GLIB_CHECK_VERSION(2,40,0)
34 #define g_info(...) g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, __VA_ARGS__)
37 // Cursor bitmaps from the installation set.
38 #define CURSOR_HANDLE_DIR "/../share/libreofficekit/"
39 // Number of handles around a graphic selection.
40 #define GRAPHIC_HANDLE_COUNT 8
41 // Maximum Zoom allowed
43 // Minimum Zoom allowed
44 #define MIN_ZOOM 0.25f
46 /// This is expected to be locked during setView(), doSomethingElse() LOK calls.
47 static std::mutex g_aLOKMutex
;
51 /// Same as a GdkRectangle, but also tracks in which part the rectangle is.
55 GdkRectangle m_aRectangle
;
57 ViewRectangle(int nPart
= 0, const GdkRectangle
& rRectangle
= GdkRectangle())
59 m_aRectangle(rRectangle
)
64 /// Same as a list of GdkRectangles, but also tracks in which part the rectangle is.
68 std::vector
<GdkRectangle
> m_aRectangles
;
70 ViewRectangles(int nPart
= 0, const std::vector
<GdkRectangle
>& rRectangles
= std::vector
<GdkRectangle
>())
72 m_aRectangles(rRectangles
)
77 /// Private struct used by this GObject type
78 struct LOKDocViewPrivateImpl
80 std::string m_aLOPath
;
81 std::string m_aUserProfileURL
;
82 std::string m_aDocPath
;
83 std::string m_aRenderingArguments
;
84 gdouble m_nLoadProgress
;
86 bool m_bInit
; // initializeForRendering() has been called
90 LibreOfficeKit
* m_pOffice
;
91 LibreOfficeKitDocument
* m_pDocument
;
93 std::unique_ptr
<TileBuffer
> m_pTileBuffer
;
94 GThreadPool
* lokThreadPool
;
97 glong m_nDocumentWidthTwips
;
98 glong m_nDocumentHeightTwips
;
99 /// View or edit mode.
102 guint64 m_nLOKFeatures
;
103 /// Number of parts in currently loaded document
105 /// Position and size of the visible cursor.
106 GdkRectangle m_aVisibleCursor
;
107 /// Position and size of the view cursors. The current view can only see
108 /// them, can't modify them. Key is the view id.
109 std::map
<int, ViewRectangle
> m_aViewCursors
;
110 /// Cursor overlay is visible or hidden (for blinking).
111 bool m_bCursorOverlayVisible
;
112 /// Cursor is visible or hidden (e.g. for graphic selection).
113 bool m_bCursorVisible
;
114 /// Visibility of view selections. The current view can only see / them,
115 /// can't modify them. Key is the view id.
116 std::map
<int, bool> m_aViewCursorVisibilities
;
117 /// Time of the last button press.
118 guint32 m_nLastButtonPressTime
;
119 /// Time of the last button release.
120 guint32 m_nLastButtonReleaseTime
;
121 /// Last pressed button (left, right, middle)
122 guint32 m_nLastButtonPressed
;
123 /// Key modifier (ctrl, atl, shift)
124 guint32 m_nKeyModifier
;
125 /// Rectangles of the current text selection.
126 std::vector
<GdkRectangle
> m_aTextSelectionRectangles
;
127 /// Rectangles of view selections. The current view can only see
128 /// them, can't modify them. Key is the view id.
129 std::map
<int, ViewRectangles
> m_aTextViewSelectionRectangles
;
130 /// Position and size of the selection start (as if there would be a cursor caret there).
131 GdkRectangle m_aTextSelectionStart
;
132 /// Position and size of the selection end.
133 GdkRectangle m_aTextSelectionEnd
;
134 GdkRectangle m_aGraphicSelection
;
135 /// Position and size of the graphic view selections. The current view can only
136 /// see them, can't modify them. Key is the view id.
137 std::map
<int, ViewRectangle
> m_aGraphicViewSelections
;
138 GdkRectangle m_aCellCursor
;
139 /// Position and size of the cell view cursors. The current view can only
140 /// see them, can't modify them. Key is the view id.
141 std::map
<int, ViewRectangle
> m_aCellViewCursors
;
142 bool m_bInDragGraphicSelection
;
143 /// Position, size and color of the reference marks. The current view can only
144 /// see them, can't modify them. Key is the view id.
145 std::vector
<std::pair
<ViewRectangle
, sal_uInt32
>> m_aReferenceMarks
;
147 /// @name Start/middle/end handle.
149 /// Bitmap of the text selection start handle.
150 cairo_surface_t
* m_pHandleStart
;
151 /// Rectangle of the text selection start handle, to know if the user clicked on it or not
152 GdkRectangle m_aHandleStartRect
;
153 /// If we are in the middle of a drag of the text selection end handle.
154 bool m_bInDragStartHandle
;
155 /// Bitmap of the text selection middle handle.
156 cairo_surface_t
* m_pHandleMiddle
;
157 /// Rectangle of the text selection middle handle, to know if the user clicked on it or not
158 GdkRectangle m_aHandleMiddleRect
;
159 /// If we are in the middle of a drag of the text selection middle handle.
160 bool m_bInDragMiddleHandle
;
161 /// Bitmap of the text selection end handle.
162 cairo_surface_t
* m_pHandleEnd
;
163 /// Rectangle of the text selection end handle, to know if the user clicked on it or not
164 GdkRectangle m_aHandleEndRect
;
165 /// If we are in the middle of a drag of the text selection end handle.
166 bool m_bInDragEndHandle
;
169 /// @name Graphic handles.
171 /// Rectangle of a graphic selection handle, to know if the user clicked on it or not.
172 GdkRectangle m_aGraphicHandleRects
[8];
173 /// If we are in the middle of a drag of a graphic selection handle.
174 bool m_bInDragGraphicHandles
[8];
177 /// View ID, returned by createView() or 0 by default.
180 /// Cached part ID, returned by getPart().
183 /// Cached document type, returned by getDocumentType().
184 LibreOfficeKitDocumentType m_eDocumentType
;
186 /// Contains a freshly set zoom level: logic size of a tile.
187 /// It gets reset back to 0 when LOK was informed about this zoom change.
188 int m_nTileSizeTwips
;
190 GdkRectangle m_aVisibleArea
;
191 bool m_bVisibleAreaSet
;
193 /// Event source ID for handleTimeout() of this widget.
196 /// Rectangles of view locks. The current view can only see
197 /// them, can't modify them. Key is the view id.
198 std::map
<int, ViewRectangle
> m_aViewLockRectangles
;
200 LOKDocViewPrivateImpl()
201 : m_nLoadProgress(0),
208 m_pDocument(nullptr),
209 lokThreadPool(nullptr),
211 m_nDocumentWidthTwips(0),
212 m_nDocumentHeightTwips(0),
216 m_aVisibleCursor({0, 0, 0, 0}),
217 m_bCursorOverlayVisible(false),
218 m_bCursorVisible(true),
219 m_nLastButtonPressTime(0),
220 m_nLastButtonReleaseTime(0),
221 m_nLastButtonPressed(0),
223 m_aTextSelectionStart({0, 0, 0, 0}),
224 m_aTextSelectionEnd({0, 0, 0, 0}),
225 m_aGraphicSelection({0, 0, 0, 0}),
226 m_aCellCursor({0, 0, 0, 0}),
227 m_bInDragGraphicSelection(false),
228 m_pHandleStart(nullptr),
229 m_aHandleStartRect({0, 0, 0, 0}),
230 m_bInDragStartHandle(false),
231 m_pHandleMiddle(nullptr),
232 m_aHandleMiddleRect({0, 0, 0, 0}),
233 m_bInDragMiddleHandle(false),
234 m_pHandleEnd(nullptr),
235 m_aHandleEndRect({0, 0, 0, 0}),
236 m_bInDragEndHandle(false),
239 m_eDocumentType(LOK_DOCTYPE_OTHER
),
241 m_aVisibleArea({0, 0, 0, 0}),
242 m_bVisibleAreaSet(false),
245 memset(&m_aGraphicHandleRects
, 0, sizeof(m_aGraphicHandleRects
));
246 memset(&m_bInDragGraphicHandles
, 0, sizeof(m_bInDragGraphicHandles
));
249 ~LOKDocViewPrivateImpl()
252 g_source_remove(m_nTimeoutId
);
258 /// Wrapper around LOKDocViewPrivateImpl, managed by malloc/memset/free.
259 struct _LOKDocViewPrivate
261 LOKDocViewPrivateImpl
* m_pImpl
;
263 LOKDocViewPrivateImpl
* operator->()
300 PROP_USER_PROFILE_URL
,
313 PROP_DOC_PASSWORD_TO_MODIFY
,
314 PROP_TILED_ANNOTATIONS
,
319 static guint doc_view_signals
[LAST_SIGNAL
] = { 0 };
320 static GParamSpec
*properties
[PROP_LAST
] = { nullptr };
322 static void lok_doc_view_initable_iface_init (GInitableIface
*iface
);
323 static void callbackWorker (int nType
, const char* pPayload
, void* pData
);
324 static void updateClientZoom (LOKDocView
*pDocView
);
326 SAL_DLLPUBLIC_EXPORT GType
lok_doc_view_get_type();
328 #pragma GCC diagnostic push
329 #pragma GCC diagnostic ignored "-Wunused-function"
330 #if defined __clang__
331 #if __has_warning("-Wdeprecated-volatile")
332 #pragma clang diagnostic ignored "-Wdeprecated-volatile"
336 G_DEFINE_TYPE_WITH_CODE (LOKDocView
, lok_doc_view
, GTK_TYPE_DRAWING_AREA
,
337 G_ADD_PRIVATE (LOKDocView
)
338 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE
, lok_doc_view_initable_iface_init
));
340 #pragma GCC diagnostic pop
343 static LOKDocViewPrivate
& getPrivate(LOKDocView
* pDocView
)
345 LOKDocViewPrivate
* priv
= static_cast<LOKDocViewPrivate
*>(lok_doc_view_get_instance_private(pDocView
));
351 /// Helper struct used to pass the data from soffice thread -> main thread.
355 std::string m_aPayload
;
356 LOKDocView
* m_pDocView
;
358 CallbackData(int nType
, const std::string
& rPayload
, LOKDocView
* pDocView
)
360 m_aPayload(rPayload
),
361 m_pDocView(pDocView
) {}
367 LOKPostCommand (LOKDocView
* pDocView
,
368 const gchar
* pCommand
,
369 const gchar
* pArguments
,
370 bool bNotifyWhenFinished
)
372 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
373 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
374 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_COMMAND
);
375 GError
* error
= nullptr;
376 pLOEvent
->m_pCommand
= g_strdup(pCommand
);
377 pLOEvent
->m_pArguments
= g_strdup(pArguments
);
378 pLOEvent
->m_bNotifyWhenFinished
= bNotifyWhenFinished
;
380 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
381 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
382 if (error
!= nullptr)
384 g_warning("Unable to call LOK_POST_COMMAND: %s", error
->message
);
385 g_clear_error(&error
);
387 g_object_unref(task
);
391 doSearch(LOKDocView
* pDocView
, const char* pText
, bool bBackwards
, bool highlightAll
)
393 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
394 if (!priv
->m_pDocument
)
397 boost::property_tree::ptree aTree
;
398 GtkWidget
* drawingWidget
= GTK_WIDGET(pDocView
);
399 GdkWindow
* drawingWindow
= gtk_widget_get_window(drawingWidget
);
402 std::shared_ptr
<cairo_region_t
> cairoVisRegion( gdk_window_get_visible_region(drawingWindow
),
403 cairo_region_destroy
);
404 cairo_rectangle_int_t cairoVisRect
;
405 cairo_region_get_rectangle(cairoVisRegion
.get(), 0, &cairoVisRect
);
406 int x
= pixelToTwip (cairoVisRect
.x
, priv
->m_fZoom
);
407 int y
= pixelToTwip (cairoVisRect
.y
, priv
->m_fZoom
);
409 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchString/type", '/'), "string");
410 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchString/value", '/'), pText
);
411 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.Backward/type", '/'), "boolean");
412 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.Backward/value", '/'), bBackwards
);
415 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.Command/type", '/'), "unsigned short");
416 // SvxSearchCmd::FIND_ALL
417 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.Command/value", '/'), "1");
420 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointX/type", '/'), "long");
421 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointX/value", '/'), x
);
422 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointY/type", '/'), "long");
423 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointY/value", '/'), y
);
425 std::stringstream aStream
;
426 boost::property_tree::write_json(aStream
, aTree
);
428 LOKPostCommand (pDocView
, ".uno:ExecuteSearch", aStream
.str().c_str(), false);
432 isEmptyRectangle(const GdkRectangle
& rRectangle
)
434 return rRectangle
.x
== 0 && rRectangle
.y
== 0 && rRectangle
.width
== 0 && rRectangle
.height
== 0;
437 /// if handled, returns TRUE else FALSE
439 handleTextSelectionOnButtonPress(GdkRectangle
& aClick
, LOKDocView
* pDocView
) {
440 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
442 if (gdk_rectangle_intersect(&aClick
, &priv
->m_aHandleStartRect
, nullptr))
444 g_info("LOKDocView_Impl::signalButton: start of drag start handle");
445 priv
->m_bInDragStartHandle
= true;
448 else if (gdk_rectangle_intersect(&aClick
, &priv
->m_aHandleMiddleRect
, nullptr))
450 g_info("LOKDocView_Impl::signalButton: start of drag middle handle");
451 priv
->m_bInDragMiddleHandle
= true;
454 else if (gdk_rectangle_intersect(&aClick
, &priv
->m_aHandleEndRect
, nullptr))
456 g_info("LOKDocView_Impl::signalButton: start of drag end handle");
457 priv
->m_bInDragEndHandle
= true;
464 /// if handled, returns TRUE else FALSE
466 handleGraphicSelectionOnButtonPress(GdkRectangle
& aClick
, LOKDocView
* pDocView
) {
467 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
468 GError
* error
= nullptr;
470 for (int i
= 0; i
< GRAPHIC_HANDLE_COUNT
; ++i
)
472 if (gdk_rectangle_intersect(&aClick
, &priv
->m_aGraphicHandleRects
[i
], nullptr))
474 g_info("LOKDocView_Impl::signalButton: start of drag graphic handle #%d", i
);
475 priv
->m_bInDragGraphicHandles
[i
] = true;
477 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
478 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_GRAPHIC_SELECTION
);
479 pLOEvent
->m_nSetGraphicSelectionType
= LOK_SETGRAPHICSELECTION_START
;
480 pLOEvent
->m_nSetGraphicSelectionX
= pixelToTwip(priv
->m_aGraphicHandleRects
[i
].x
+ priv
->m_aGraphicHandleRects
[i
].width
/ 2, priv
->m_fZoom
);
481 pLOEvent
->m_nSetGraphicSelectionY
= pixelToTwip(priv
->m_aGraphicHandleRects
[i
].y
+ priv
->m_aGraphicHandleRects
[i
].height
/ 2, priv
->m_fZoom
);
482 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
484 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
485 if (error
!= nullptr)
487 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error
->message
);
488 g_clear_error(&error
);
490 g_object_unref(task
);
499 /// if handled, returns TRUE else FALSE
501 handleTextSelectionOnButtonRelease(LOKDocView
* pDocView
) {
502 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
504 if (priv
->m_bInDragStartHandle
)
506 g_info("LOKDocView_Impl::signalButton: end of drag start handle");
507 priv
->m_bInDragStartHandle
= false;
510 else if (priv
->m_bInDragMiddleHandle
)
512 g_info("LOKDocView_Impl::signalButton: end of drag middle handle");
513 priv
->m_bInDragMiddleHandle
= false;
516 else if (priv
->m_bInDragEndHandle
)
518 g_info("LOKDocView_Impl::signalButton: end of drag end handle");
519 priv
->m_bInDragEndHandle
= false;
526 /// if handled, returns TRUE else FALSE
528 handleGraphicSelectionOnButtonRelease(LOKDocView
* pDocView
, GdkEventButton
* pEvent
) {
529 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
530 GError
* error
= nullptr;
532 for (int i
= 0; i
< GRAPHIC_HANDLE_COUNT
; ++i
)
534 if (priv
->m_bInDragGraphicHandles
[i
])
536 g_info("LOKDocView_Impl::signalButton: end of drag graphic handle #%d", i
);
537 priv
->m_bInDragGraphicHandles
[i
] = false;
539 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
540 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_GRAPHIC_SELECTION
);
541 pLOEvent
->m_nSetGraphicSelectionType
= LOK_SETGRAPHICSELECTION_END
;
542 pLOEvent
->m_nSetGraphicSelectionX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
543 pLOEvent
->m_nSetGraphicSelectionY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
544 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
546 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
547 if (error
!= nullptr)
549 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error
->message
);
550 g_clear_error(&error
);
552 g_object_unref(task
);
558 if (priv
->m_bInDragGraphicSelection
)
560 g_info("LOKDocView_Impl::signalButton: end of drag graphic selection");
561 priv
->m_bInDragGraphicSelection
= false;
563 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
564 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_GRAPHIC_SELECTION
);
565 pLOEvent
->m_nSetGraphicSelectionType
= LOK_SETGRAPHICSELECTION_END
;
566 pLOEvent
->m_nSetGraphicSelectionX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
567 pLOEvent
->m_nSetGraphicSelectionY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
568 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
570 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
571 if (error
!= nullptr)
573 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error
->message
);
574 g_clear_error(&error
);
576 g_object_unref(task
);
585 postKeyEventInThread(gpointer data
)
587 GTask
* task
= G_TASK(data
);
588 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
589 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
590 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
591 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
592 gint nTileSizePixelsScaled
= nTileSizePixels
* nScaleFactor
;
594 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
595 std::stringstream ss
;
596 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
597 g_info("%s", ss
.str().c_str());
598 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
600 if (priv
->m_nTileSizeTwips
)
602 ss
.str(std::string());
603 ss
<< "lok::Document::setClientZoom(" << nTileSizePixelsScaled
<< ", " << nTileSizePixelsScaled
<< ", " << priv
->m_nTileSizeTwips
<< ", " << priv
->m_nTileSizeTwips
<< ")";
604 g_info("%s", ss
.str().c_str());
605 priv
->m_pDocument
->pClass
->setClientZoom(priv
->m_pDocument
,
606 nTileSizePixelsScaled
,
607 nTileSizePixelsScaled
,
608 priv
->m_nTileSizeTwips
,
609 priv
->m_nTileSizeTwips
);
610 priv
->m_nTileSizeTwips
= 0;
612 if (priv
->m_bVisibleAreaSet
)
614 ss
.str(std::string());
615 ss
<< "lok::Document::setClientVisibleArea(" << priv
->m_aVisibleArea
.x
<< ", " << priv
->m_aVisibleArea
.y
<< ", ";
616 ss
<< priv
->m_aVisibleArea
.width
<< ", " << priv
->m_aVisibleArea
.height
<< ")";
617 g_info("%s", ss
.str().c_str());
618 priv
->m_pDocument
->pClass
->setClientVisibleArea(priv
->m_pDocument
,
619 priv
->m_aVisibleArea
.x
,
620 priv
->m_aVisibleArea
.y
,
621 priv
->m_aVisibleArea
.width
,
622 priv
->m_aVisibleArea
.height
);
623 priv
->m_bVisibleAreaSet
= false;
626 ss
.str(std::string());
627 ss
<< "lok::Document::postKeyEvent(" << pLOEvent
->m_nKeyEvent
<< ", " << pLOEvent
->m_nCharCode
<< ", " << pLOEvent
->m_nKeyCode
<< ")";
628 g_info("%s", ss
.str().c_str());
629 priv
->m_pDocument
->pClass
->postKeyEvent(priv
->m_pDocument
,
630 pLOEvent
->m_nKeyEvent
,
631 pLOEvent
->m_nCharCode
,
632 pLOEvent
->m_nKeyCode
);
636 signalKey (GtkWidget
* pWidget
, GdkEventKey
* pEvent
)
638 LOKDocView
* pDocView
= LOK_DOC_VIEW(pWidget
);
639 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
642 GError
* error
= nullptr;
646 g_info("signalKey: not in edit mode, ignore");
650 priv
->m_nKeyModifier
&= KEY_MOD2
;
651 switch (pEvent
->keyval
)
653 case GDK_KEY_BackSpace
:
654 nKeyCode
= com::sun::star::awt::Key::BACKSPACE
;
657 nKeyCode
= com::sun::star::awt::Key::DELETE
;
660 case GDK_KEY_KP_Enter
:
661 nKeyCode
= com::sun::star::awt::Key::RETURN
;
664 nKeyCode
= com::sun::star::awt::Key::ESCAPE
;
667 nKeyCode
= com::sun::star::awt::Key::TAB
;
670 nKeyCode
= com::sun::star::awt::Key::DOWN
;
673 nKeyCode
= com::sun::star::awt::Key::UP
;
676 nKeyCode
= com::sun::star::awt::Key::LEFT
;
679 nKeyCode
= com::sun::star::awt::Key::RIGHT
;
681 case GDK_KEY_Page_Down
:
682 nKeyCode
= com::sun::star::awt::Key::PAGEDOWN
;
684 case GDK_KEY_Page_Up
:
685 nKeyCode
= com::sun::star::awt::Key::PAGEUP
;
688 nKeyCode
= com::sun::star::awt::Key::INSERT
;
690 case GDK_KEY_Shift_L
:
691 case GDK_KEY_Shift_R
:
692 if (pEvent
->type
== GDK_KEY_PRESS
)
693 priv
->m_nKeyModifier
|= KEY_SHIFT
;
695 case GDK_KEY_Control_L
:
696 case GDK_KEY_Control_R
:
697 if (pEvent
->type
== GDK_KEY_PRESS
)
698 priv
->m_nKeyModifier
|= KEY_MOD1
;
702 if (pEvent
->type
== GDK_KEY_PRESS
)
703 priv
->m_nKeyModifier
|= KEY_MOD2
;
705 priv
->m_nKeyModifier
&= ~KEY_MOD2
;
708 if (pEvent
->keyval
>= GDK_KEY_F1
&& pEvent
->keyval
<= GDK_KEY_F26
)
709 nKeyCode
= com::sun::star::awt::Key::F1
+ (pEvent
->keyval
- GDK_KEY_F1
);
711 nCharCode
= gdk_keyval_to_unicode(pEvent
->keyval
);
714 // rsc is not public API, but should be good enough for debugging purposes.
715 // If this is needed for real, then probably a new param of type
716 // css::awt::KeyModifier is needed in postKeyEvent().
717 if (pEvent
->state
& GDK_SHIFT_MASK
)
718 nKeyCode
|= KEY_SHIFT
;
720 if (pEvent
->state
& GDK_CONTROL_MASK
)
721 nKeyCode
|= KEY_MOD1
;
723 if (priv
->m_nKeyModifier
& KEY_MOD2
)
724 nKeyCode
|= KEY_MOD2
;
726 if (nKeyCode
& (KEY_SHIFT
| KEY_MOD1
| KEY_MOD2
)) {
727 if (pEvent
->keyval
>= GDK_KEY_a
&& pEvent
->keyval
<= GDK_KEY_z
)
729 nKeyCode
|= 512 + (pEvent
->keyval
- GDK_KEY_a
);
731 else if (pEvent
->keyval
>= GDK_KEY_A
&& pEvent
->keyval
<= GDK_KEY_Z
) {
732 nKeyCode
|= 512 + (pEvent
->keyval
- GDK_KEY_A
);
734 else if (pEvent
->keyval
>= GDK_KEY_0
&& pEvent
->keyval
<= GDK_KEY_9
) {
735 nKeyCode
|= 256 + (pEvent
->keyval
- GDK_KEY_0
);
739 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
740 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_KEY
);
741 pLOEvent
->m_nKeyEvent
= pEvent
->type
== GDK_KEY_RELEASE
? LOK_KEYEVENT_KEYUP
: LOK_KEYEVENT_KEYINPUT
;
742 pLOEvent
->m_nCharCode
= nCharCode
;
743 pLOEvent
->m_nKeyCode
= nKeyCode
;
744 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
745 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
746 if (error
!= nullptr)
748 g_warning("Unable to call LOK_POST_KEY: %s", error
->message
);
749 g_clear_error(&error
);
751 g_object_unref(task
);
757 handleTimeout (gpointer pData
)
759 LOKDocView
* pDocView
= LOK_DOC_VIEW (pData
);
760 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
764 if (priv
->m_bCursorOverlayVisible
)
765 priv
->m_bCursorOverlayVisible
= false;
767 priv
->m_bCursorOverlayVisible
= true;
768 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
771 return G_SOURCE_CONTINUE
;
775 commandChanged(LOKDocView
* pDocView
, const std::string
& rString
)
777 g_signal_emit(pDocView
, doc_view_signals
[COMMAND_CHANGED
], 0, rString
.c_str());
781 searchNotFound(LOKDocView
* pDocView
, const std::string
& rString
)
783 g_signal_emit(pDocView
, doc_view_signals
[SEARCH_NOT_FOUND
], 0, rString
.c_str());
786 static void searchResultCount(LOKDocView
* pDocView
, const std::string
& rString
)
788 g_signal_emit(pDocView
, doc_view_signals
[SEARCH_RESULT_COUNT
], 0, rString
.c_str());
791 static void commandResult(LOKDocView
* pDocView
, const std::string
& rString
)
793 g_signal_emit(pDocView
, doc_view_signals
[COMMAND_RESULT
], 0, rString
.c_str());
796 static void addressChanged(LOKDocView
* pDocView
, const std::string
& rString
)
798 g_signal_emit(pDocView
, doc_view_signals
[ADDRESS_CHANGED
], 0, rString
.c_str());
801 static void formulaChanged(LOKDocView
* pDocView
, const std::string
& rString
)
803 g_signal_emit(pDocView
, doc_view_signals
[FORMULA_CHANGED
], 0, rString
.c_str());
806 static void reportError(LOKDocView
* /*pDocView*/, const std::string
& rString
)
808 GtkWidget
*dialog
= gtk_message_dialog_new(nullptr,
809 GTK_DIALOG_DESTROY_WITH_PARENT
,
814 gtk_dialog_run(GTK_DIALOG(dialog
));
815 gtk_widget_destroy(dialog
);
819 setPart(LOKDocView
* pDocView
, const std::string
& rString
)
821 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
822 priv
->m_nPartId
= std::stoi(rString
);
823 g_signal_emit(pDocView
, doc_view_signals
[PART_CHANGED
], 0, priv
->m_nPartId
);
827 hyperlinkClicked(LOKDocView
* pDocView
, const std::string
& rString
)
829 g_signal_emit(pDocView
, doc_view_signals
[HYPERLINK_CLICKED
], 0, rString
.c_str());
832 /// Trigger a redraw, invoked on the main thread by other functions running in a thread.
833 static gboolean
queueDraw(gpointer pData
)
835 GtkWidget
* pWidget
= static_cast<GtkWidget
*>(pData
);
837 gtk_widget_queue_draw(pWidget
);
839 return G_SOURCE_REMOVE
;
842 /// Looks up the author string from initializeForRendering()'s rendering arguments.
843 static std::string
getAuthorRenderingArgument(LOKDocViewPrivate
& priv
)
845 std::stringstream aStream
;
846 aStream
<< priv
->m_aRenderingArguments
;
847 boost::property_tree::ptree aTree
;
848 boost::property_tree::read_json(aStream
, aTree
);
850 for (const auto& rPair
: aTree
)
852 if (rPair
.first
== ".uno:Author")
854 aRet
= rPair
.second
.get
<std::string
>("value");
861 /// Author string <-> View ID map
862 static std::map
<std::string
, int> g_aAuthorViews
;
864 static void refreshSize(LOKDocView
* pDocView
)
866 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
868 priv
->m_pDocument
->pClass
->getDocumentSize(priv
->m_pDocument
, &priv
->m_nDocumentWidthTwips
, &priv
->m_nDocumentHeightTwips
);
869 float zoom
= priv
->m_fZoom
;
870 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
871 gint nTileSizePixelsScaled
= nTileSizePixels
* nScaleFactor
;
872 long nDocumentWidthTwips
= priv
->m_nDocumentWidthTwips
;
873 long nDocumentHeightTwips
= priv
->m_nDocumentHeightTwips
;
874 long nDocumentWidthPixels
= twipToPixel(nDocumentWidthTwips
, zoom
);
875 long nDocumentHeightPixels
= twipToPixel(nDocumentHeightTwips
, zoom
);
877 // Total number of columns in this document.
878 guint nColumns
= ceil(static_cast<double>(nDocumentWidthPixels
) / nTileSizePixelsScaled
);
879 priv
->m_pTileBuffer
= std::make_unique
<TileBuffer
>(nColumns
, nScaleFactor
);
880 gtk_widget_set_size_request(GTK_WIDGET(pDocView
),
881 nDocumentWidthPixels
,
882 nDocumentHeightPixels
);
885 /// Set up LOKDocView after the document is loaded, invoked on the main thread by openDocumentInThread() running in a thread.
886 static gboolean
postDocumentLoad(gpointer pData
)
888 LOKDocView
* pLOKDocView
= static_cast<LOKDocView
*>(pData
);
889 LOKDocViewPrivate
& priv
= getPrivate(pLOKDocView
);
891 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
892 priv
->m_pDocument
->pClass
->initializeForRendering(priv
->m_pDocument
, priv
->m_aRenderingArguments
.c_str());
893 // This returns the view id of the "current" view, but sadly if you load multiple documents that
894 // is apparently not a view showing the most recently loaded document. Not much we can do here,
895 // though. If that is fixed, this comment becomes incorrect.
896 priv
->m_nViewId
= priv
->m_pDocument
->pClass
->getView(priv
->m_pDocument
);
897 g_aAuthorViews
[getAuthorRenderingArgument(priv
)] = priv
->m_nViewId
;
898 priv
->m_pDocument
->pClass
->registerCallback(priv
->m_pDocument
, callbackWorker
, pLOKDocView
);
899 priv
->m_nParts
= priv
->m_pDocument
->pClass
->getParts(priv
->m_pDocument
);
901 priv
->m_nTimeoutId
= g_timeout_add(600, handleTimeout
, pLOKDocView
);
903 refreshSize(pLOKDocView
);
905 gtk_widget_set_can_focus(GTK_WIDGET(pLOKDocView
), true);
906 gtk_widget_grab_focus(GTK_WIDGET(pLOKDocView
));
907 lok_doc_view_set_zoom(pLOKDocView
, 1.0);
909 // we are completely loaded
910 priv
->m_bInit
= true;
911 g_object_notify_by_pspec(G_OBJECT(pLOKDocView
), properties
[PROP_IS_INITIALIZED
]);
913 return G_SOURCE_REMOVE
;
916 /// Implementation of the global callback handler, invoked by globalCallback();
918 globalCallback (gpointer pData
)
920 CallbackData
* pCallback
= static_cast<CallbackData
*>(pData
);
921 LOKDocViewPrivate
& priv
= getPrivate(pCallback
->m_pDocView
);
922 bool bModify
= false;
924 switch (pCallback
->m_nType
)
926 case LOK_CALLBACK_STATUS_INDICATOR_START
:
928 priv
->m_nLoadProgress
= 0.0;
929 g_signal_emit (pCallback
->m_pDocView
, doc_view_signals
[LOAD_CHANGED
], 0, 0.0);
932 case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE
:
934 priv
->m_nLoadProgress
= static_cast<gdouble
>(std::stoi(pCallback
->m_aPayload
)/100.0);
935 g_signal_emit (pCallback
->m_pDocView
, doc_view_signals
[LOAD_CHANGED
], 0, priv
->m_nLoadProgress
);
938 case LOK_CALLBACK_STATUS_INDICATOR_FINISH
:
940 priv
->m_nLoadProgress
= 1.0;
941 g_signal_emit (pCallback
->m_pDocView
, doc_view_signals
[LOAD_CHANGED
], 0, 1.0);
944 case LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY
:
947 case LOK_CALLBACK_DOCUMENT_PASSWORD
:
949 char const*const pURL(pCallback
->m_aPayload
.c_str());
950 g_signal_emit (pCallback
->m_pDocView
, doc_view_signals
[PASSWORD_REQUIRED
], 0, pURL
, bModify
);
953 case LOK_CALLBACK_ERROR
:
955 reportError(pCallback
->m_pDocView
, pCallback
->m_aPayload
);
958 case LOK_CALLBACK_SIGNATURE_STATUS
:
969 return G_SOURCE_REMOVE
;
973 globalCallbackWorker(int nType
, const char* pPayload
, void* pData
)
975 LOKDocView
* pDocView
= LOK_DOC_VIEW (pData
);
977 CallbackData
* pCallback
= new CallbackData(nType
, pPayload
? pPayload
: "(nil)", pDocView
);
978 g_info("LOKDocView_Impl::globalCallbackWorkerImpl: %s, '%s'", lokCallbackTypeToString(nType
), pPayload
);
979 gdk_threads_add_idle(globalCallback
, pCallback
);
983 payloadToRectangle (LOKDocView
* pDocView
, const char* pPayload
)
985 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
987 // x, y, width, height, part number.
988 gchar
** ppCoordinates
= g_strsplit(pPayload
, ", ", 5);
989 gchar
** ppCoordinate
= ppCoordinates
;
991 aRet
.width
= aRet
.height
= aRet
.x
= aRet
.y
= 0;
995 g_strfreev(ppCoordinates
);
998 aRet
.x
= atoi(*ppCoordinate
);
1004 g_strfreev(ppCoordinates
);
1007 aRet
.y
= atoi(*ppCoordinate
);
1013 g_strfreev(ppCoordinates
);
1016 long l
= atol(*ppCoordinate
);
1017 if (l
> std::numeric_limits
<int>::max())
1018 aRet
.width
= std::numeric_limits
<int>::max();
1021 if (aRet
.x
+ aRet
.width
> priv
->m_nDocumentWidthTwips
)
1022 aRet
.width
= priv
->m_nDocumentWidthTwips
- aRet
.x
;
1026 g_strfreev(ppCoordinates
);
1029 l
= atol(*ppCoordinate
);
1030 if (l
> std::numeric_limits
<int>::max())
1031 aRet
.height
= std::numeric_limits
<int>::max();
1034 if (aRet
.y
+ aRet
.height
> priv
->m_nDocumentHeightTwips
)
1035 aRet
.height
= priv
->m_nDocumentHeightTwips
- aRet
.y
;
1037 g_strfreev(ppCoordinates
);
1041 static std::vector
<GdkRectangle
>
1042 payloadToRectangles(LOKDocView
* pDocView
, const char* pPayload
)
1044 std::vector
<GdkRectangle
> aRet
;
1046 if (g_strcmp0(pPayload
, "EMPTY") == 0)
1049 gchar
** ppRectangles
= g_strsplit(pPayload
, "; ", 0);
1050 for (gchar
** ppRectangle
= ppRectangles
; *ppRectangle
; ++ppRectangle
)
1051 aRet
.push_back(payloadToRectangle(pDocView
, *ppRectangle
));
1052 g_strfreev(ppRectangles
);
1059 setTilesInvalid (LOKDocView
* pDocView
, const GdkRectangle
& rRectangle
)
1061 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1062 GdkRectangle aRectanglePixels
;
1063 GdkPoint aStart
, aEnd
;
1064 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
1065 gint nTileSizePixelsScaled
= nTileSizePixels
* nScaleFactor
;
1067 aRectanglePixels
.x
= twipToPixel(rRectangle
.x
, priv
->m_fZoom
) * nScaleFactor
;
1068 aRectanglePixels
.y
= twipToPixel(rRectangle
.y
, priv
->m_fZoom
) * nScaleFactor
;
1069 aRectanglePixels
.width
= twipToPixel(rRectangle
.width
, priv
->m_fZoom
) * nScaleFactor
;
1070 aRectanglePixels
.height
= twipToPixel(rRectangle
.height
, priv
->m_fZoom
) * nScaleFactor
;
1072 aStart
.x
= aRectanglePixels
.y
/ nTileSizePixelsScaled
;
1073 aStart
.y
= aRectanglePixels
.x
/ nTileSizePixelsScaled
;
1074 aEnd
.x
= (aRectanglePixels
.y
+ aRectanglePixels
.height
+ nTileSizePixelsScaled
) / nTileSizePixelsScaled
;
1075 aEnd
.y
= (aRectanglePixels
.x
+ aRectanglePixels
.width
+ nTileSizePixelsScaled
) / nTileSizePixelsScaled
;
1076 for (int i
= aStart
.x
; i
< aEnd
.x
; i
++)
1078 for (int j
= aStart
.y
; j
< aEnd
.y
; j
++)
1080 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
1081 priv
->m_pTileBuffer
->setInvalid(i
, j
, priv
->m_fZoom
, task
, priv
->lokThreadPool
);
1082 g_object_unref(task
);
1088 callback (gpointer pData
)
1090 CallbackData
* pCallback
= static_cast<CallbackData
*>(pData
);
1091 LOKDocView
* pDocView
= LOK_DOC_VIEW (pCallback
->m_pDocView
);
1092 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1094 //callback registered before the widget was destroyed.
1095 //Use existence of lokThreadPool as flag it was torn down
1096 if (!priv
->lokThreadPool
)
1099 return G_SOURCE_REMOVE
;
1102 switch (static_cast<LibreOfficeKitCallbackType
>(pCallback
->m_nType
))
1104 case LOK_CALLBACK_INVALIDATE_TILES
:
1106 if (pCallback
->m_aPayload
.compare(0, 5, "EMPTY") != 0) // payload doesn't start with "EMPTY"
1108 GdkRectangle aRectangle
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1109 setTilesInvalid(pDocView
, aRectangle
);
1112 priv
->m_pTileBuffer
->resetAllTiles();
1114 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1117 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
:
1120 std::stringstream
aStream(pCallback
->m_aPayload
);
1121 boost::property_tree::ptree aTree
;
1122 boost::property_tree::read_json(aStream
, aTree
);
1123 const std::string
& rRectangle
= aTree
.get
<std::string
>("rectangle");
1124 int nViewId
= aTree
.get
<int>("viewId");
1126 priv
->m_aVisibleCursor
= payloadToRectangle(pDocView
, rRectangle
.c_str());
1127 priv
->m_bCursorOverlayVisible
= true;
1128 if(nViewId
== priv
->m_nViewId
)
1130 g_signal_emit(pDocView
, doc_view_signals
[CURSOR_CHANGED
], 0,
1131 priv
->m_aVisibleCursor
.x
,
1132 priv
->m_aVisibleCursor
.y
,
1133 priv
->m_aVisibleCursor
.width
,
1134 priv
->m_aVisibleCursor
.height
);
1136 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1139 case LOK_CALLBACK_TEXT_SELECTION
:
1141 priv
->m_aTextSelectionRectangles
= payloadToRectangles(pDocView
, pCallback
->m_aPayload
.c_str());
1142 bool bIsTextSelected
= !priv
->m_aTextSelectionRectangles
.empty();
1143 // In case the selection is empty, then we get no LOK_CALLBACK_TEXT_SELECTION_START/END events.
1144 if (!bIsTextSelected
)
1146 memset(&priv
->m_aTextSelectionStart
, 0, sizeof(priv
->m_aTextSelectionStart
));
1147 memset(&priv
->m_aHandleStartRect
, 0, sizeof(priv
->m_aHandleStartRect
));
1148 memset(&priv
->m_aTextSelectionEnd
, 0, sizeof(priv
->m_aTextSelectionEnd
));
1149 memset(&priv
->m_aHandleEndRect
, 0, sizeof(priv
->m_aHandleEndRect
));
1152 memset(&priv
->m_aHandleMiddleRect
, 0, sizeof(priv
->m_aHandleMiddleRect
));
1154 g_signal_emit(pDocView
, doc_view_signals
[TEXT_SELECTION
], 0, bIsTextSelected
);
1155 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1158 case LOK_CALLBACK_TEXT_SELECTION_START
:
1160 priv
->m_aTextSelectionStart
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1163 case LOK_CALLBACK_TEXT_SELECTION_END
:
1165 priv
->m_aTextSelectionEnd
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1168 case LOK_CALLBACK_CURSOR_VISIBLE
:
1170 priv
->m_bCursorVisible
= pCallback
->m_aPayload
== "true";
1173 case LOK_CALLBACK_MOUSE_POINTER
:
1175 // We do not want the cursor to get changed in view-only mode
1178 // The gtk docs claim that most css cursors should be supported, however
1179 // on my system at least this is not true and many cursors are unsupported.
1180 // In this case pCursor = null, which results in the default cursor
1182 GdkCursor
* pCursor
= gdk_cursor_new_from_name(gtk_widget_get_display(GTK_WIDGET(pDocView
)),
1183 pCallback
->m_aPayload
.c_str());
1184 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(pDocView
)), pCursor
);
1188 case LOK_CALLBACK_GRAPHIC_SELECTION
:
1190 if (pCallback
->m_aPayload
!= "EMPTY")
1191 priv
->m_aGraphicSelection
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1193 memset(&priv
->m_aGraphicSelection
, 0, sizeof(priv
->m_aGraphicSelection
));
1194 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1197 case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION
:
1199 std::stringstream
aStream(pCallback
->m_aPayload
);
1200 boost::property_tree::ptree aTree
;
1201 boost::property_tree::read_json(aStream
, aTree
);
1202 int nViewId
= aTree
.get
<int>("viewId");
1203 int nPart
= aTree
.get
<int>("part");
1204 const std::string
& rRectangle
= aTree
.get
<std::string
>("selection");
1205 if (rRectangle
!= "EMPTY")
1206 priv
->m_aGraphicViewSelections
[nViewId
] = ViewRectangle(nPart
, payloadToRectangle(pDocView
, rRectangle
.c_str()));
1209 auto it
= priv
->m_aGraphicViewSelections
.find(nViewId
);
1210 if (it
!= priv
->m_aGraphicViewSelections
.end())
1211 priv
->m_aGraphicViewSelections
.erase(it
);
1213 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1217 case LOK_CALLBACK_CELL_CURSOR
:
1219 if (pCallback
->m_aPayload
!= "EMPTY")
1220 priv
->m_aCellCursor
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1222 memset(&priv
->m_aCellCursor
, 0, sizeof(priv
->m_aCellCursor
));
1223 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1226 case LOK_CALLBACK_HYPERLINK_CLICKED
:
1228 hyperlinkClicked(pDocView
, pCallback
->m_aPayload
);
1231 case LOK_CALLBACK_STATE_CHANGED
:
1233 commandChanged(pDocView
, pCallback
->m_aPayload
);
1236 case LOK_CALLBACK_SEARCH_NOT_FOUND
:
1238 searchNotFound(pDocView
, pCallback
->m_aPayload
);
1241 case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED
:
1243 refreshSize(pDocView
);
1244 g_signal_emit(pDocView
, doc_view_signals
[SIZE_CHANGED
], 0, nullptr);
1247 case LOK_CALLBACK_SET_PART
:
1249 setPart(pDocView
, pCallback
->m_aPayload
);
1252 case LOK_CALLBACK_SEARCH_RESULT_SELECTION
:
1254 boost::property_tree::ptree aTree
;
1255 std::stringstream
aStream(pCallback
->m_aPayload
);
1256 boost::property_tree::read_json(aStream
, aTree
);
1257 int nCount
= aTree
.get_child("searchResultSelection").size();
1258 searchResultCount(pDocView
, std::to_string(nCount
));
1261 case LOK_CALLBACK_UNO_COMMAND_RESULT
:
1263 commandResult(pDocView
, pCallback
->m_aPayload
);
1266 case LOK_CALLBACK_CELL_ADDRESS
:
1268 addressChanged(pDocView
, pCallback
->m_aPayload
);
1271 case LOK_CALLBACK_CELL_FORMULA
:
1273 formulaChanged(pDocView
, pCallback
->m_aPayload
);
1276 case LOK_CALLBACK_ERROR
:
1278 reportError(pDocView
, pCallback
->m_aPayload
);
1281 case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR
:
1283 std::stringstream
aStream(pCallback
->m_aPayload
);
1284 boost::property_tree::ptree aTree
;
1285 boost::property_tree::read_json(aStream
, aTree
);
1286 int nViewId
= aTree
.get
<int>("viewId");
1287 int nPart
= aTree
.get
<int>("part");
1288 const std::string
& rRectangle
= aTree
.get
<std::string
>("rectangle");
1289 priv
->m_aViewCursors
[nViewId
] = ViewRectangle(nPart
, payloadToRectangle(pDocView
, rRectangle
.c_str()));
1290 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1293 case LOK_CALLBACK_TEXT_VIEW_SELECTION
:
1295 std::stringstream
aStream(pCallback
->m_aPayload
);
1296 boost::property_tree::ptree aTree
;
1297 boost::property_tree::read_json(aStream
, aTree
);
1298 int nViewId
= aTree
.get
<int>("viewId");
1299 int nPart
= aTree
.get
<int>("part");
1300 const std::string
& rSelection
= aTree
.get
<std::string
>("selection");
1301 priv
->m_aTextViewSelectionRectangles
[nViewId
] = ViewRectangles(nPart
, payloadToRectangles(pDocView
, rSelection
.c_str()));
1302 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1305 case LOK_CALLBACK_VIEW_CURSOR_VISIBLE
:
1307 std::stringstream
aStream(pCallback
->m_aPayload
);
1308 boost::property_tree::ptree aTree
;
1309 boost::property_tree::read_json(aStream
, aTree
);
1310 int nViewId
= aTree
.get
<int>("viewId");
1311 const std::string
& rVisible
= aTree
.get
<std::string
>("visible");
1312 priv
->m_aViewCursorVisibilities
[nViewId
] = rVisible
== "true";
1313 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1317 case LOK_CALLBACK_CELL_VIEW_CURSOR
:
1319 std::stringstream
aStream(pCallback
->m_aPayload
);
1320 boost::property_tree::ptree aTree
;
1321 boost::property_tree::read_json(aStream
, aTree
);
1322 int nViewId
= aTree
.get
<int>("viewId");
1323 int nPart
= aTree
.get
<int>("part");
1324 const std::string
& rRectangle
= aTree
.get
<std::string
>("rectangle");
1325 if (rRectangle
!= "EMPTY")
1326 priv
->m_aCellViewCursors
[nViewId
] = ViewRectangle(nPart
, payloadToRectangle(pDocView
, rRectangle
.c_str()));
1329 auto it
= priv
->m_aCellViewCursors
.find(nViewId
);
1330 if (it
!= priv
->m_aCellViewCursors
.end())
1331 priv
->m_aCellViewCursors
.erase(it
);
1333 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1336 case LOK_CALLBACK_VIEW_LOCK
:
1338 std::stringstream
aStream(pCallback
->m_aPayload
);
1339 boost::property_tree::ptree aTree
;
1340 boost::property_tree::read_json(aStream
, aTree
);
1341 int nViewId
= aTree
.get
<int>("viewId");
1342 int nPart
= aTree
.get
<int>("part");
1343 const std::string
& rRectangle
= aTree
.get
<std::string
>("rectangle");
1344 if (rRectangle
!= "EMPTY")
1345 priv
->m_aViewLockRectangles
[nViewId
] = ViewRectangle(nPart
, payloadToRectangle(pDocView
, rRectangle
.c_str()));
1348 auto it
= priv
->m_aViewLockRectangles
.find(nViewId
);
1349 if (it
!= priv
->m_aViewLockRectangles
.end())
1350 priv
->m_aViewLockRectangles
.erase(it
);
1352 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1355 case LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED
:
1359 case LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED
:
1363 case LOK_CALLBACK_COMMENT
:
1364 g_signal_emit(pCallback
->m_pDocView
, doc_view_signals
[COMMENT
], 0, pCallback
->m_aPayload
.c_str());
1366 case LOK_CALLBACK_RULER_UPDATE
:
1367 g_signal_emit(pCallback
->m_pDocView
, doc_view_signals
[RULER
], 0, pCallback
->m_aPayload
.c_str());
1369 case LOK_CALLBACK_WINDOW
:
1370 g_signal_emit(pCallback
->m_pDocView
, doc_view_signals
[WINDOW
], 0, pCallback
->m_aPayload
.c_str());
1372 case LOK_CALLBACK_INVALIDATE_HEADER
:
1373 g_signal_emit(pCallback
->m_pDocView
, doc_view_signals
[INVALIDATE_HEADER
], 0, pCallback
->m_aPayload
.c_str());
1375 case LOK_CALLBACK_REFERENCE_MARKS
:
1377 std::stringstream
aStream(pCallback
->m_aPayload
);
1378 boost::property_tree::ptree aTree
;
1379 boost::property_tree::read_json(aStream
, aTree
);
1381 priv
->m_aReferenceMarks
.clear();
1383 for(const auto& rMark
: aTree
.get_child("marks"))
1385 sal_uInt32 nColor
= std::stoi(rMark
.second
.get
<std::string
>("color"), nullptr, 16);
1386 std::string sRect
= rMark
.second
.get
<std::string
>("rectangle");
1387 sal_uInt32 nPart
= std::stoi(rMark
.second
.get
<std::string
>("part"));
1389 GdkRectangle aRect
= payloadToRectangle(pDocView
, sRect
.c_str());
1390 priv
->m_aReferenceMarks
.push_back(std::pair
<ViewRectangle
, sal_uInt32
>(ViewRectangle(nPart
, aRect
), nColor
));
1393 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1397 case LOK_CALLBACK_STATUS_INDICATOR_START
:
1398 case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE
:
1399 case LOK_CALLBACK_STATUS_INDICATOR_FINISH
:
1400 case LOK_CALLBACK_DOCUMENT_PASSWORD
:
1401 case LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY
:
1402 case LOK_CALLBACK_VALIDITY_LIST_BUTTON
:
1403 case LOK_CALLBACK_VALIDITY_INPUT_HELP
:
1404 case LOK_CALLBACK_SIGNATURE_STATUS
:
1405 case LOK_CALLBACK_CONTEXT_MENU
:
1406 case LOK_CALLBACK_PROFILE_FRAME
:
1407 case LOK_CALLBACK_CLIPBOARD_CHANGED
:
1408 case LOK_CALLBACK_CONTEXT_CHANGED
:
1409 case LOK_CALLBACK_CELL_SELECTION_AREA
:
1410 case LOK_CALLBACK_CELL_AUTO_FILL_AREA
:
1411 case LOK_CALLBACK_TABLE_SELECTED
:
1412 case LOK_CALLBACK_JSDIALOG
:
1413 case LOK_CALLBACK_CALC_FUNCTION_LIST
:
1414 case LOK_CALLBACK_TAB_STOP_LIST
:
1415 case LOK_CALLBACK_FORM_FIELD_BUTTON
:
1416 case LOK_CALLBACK_INVALIDATE_SHEET_GEOMETRY
:
1418 // TODO: Implement me
1424 return G_SOURCE_REMOVE
;
1427 static void callbackWorker (int nType
, const char* pPayload
, void* pData
)
1429 LOKDocView
* pDocView
= LOK_DOC_VIEW (pData
);
1431 CallbackData
* pCallback
= new CallbackData(nType
, pPayload
? pPayload
: "(nil)", pDocView
);
1432 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1433 std::stringstream ss
;
1434 ss
<< "callbackWorker, view #" << priv
->m_nViewId
<< ": " << lokCallbackTypeToString(nType
) << ", '" << (pPayload
? pPayload
: "(nil)") << "'";
1435 g_info("%s", ss
.str().c_str());
1436 gdk_threads_add_idle(callback
, pCallback
);
1440 renderHandle(LOKDocView
* pDocView
,
1442 const GdkRectangle
& rCursor
,
1443 cairo_surface_t
* pHandle
,
1444 GdkRectangle
& rRectangle
)
1446 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1447 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
1448 GdkPoint aCursorBottom
;
1449 int nHandleWidth
, nHandleHeight
;
1450 double fHandleScale
;
1452 nHandleWidth
= cairo_image_surface_get_width(pHandle
);
1453 nHandleHeight
= cairo_image_surface_get_height(pHandle
);
1454 // We want to scale down the handle, so that its height is the same as the cursor caret.
1455 fHandleScale
= twipToPixel(rCursor
.height
, priv
->m_fZoom
) / nHandleHeight
;
1456 // We want the top center of the handle bitmap to be at the bottom center of the cursor rectangle.
1457 aCursorBottom
.x
= twipToPixel(rCursor
.x
, priv
->m_fZoom
) + twipToPixel(rCursor
.width
, priv
->m_fZoom
) / 2 - (nHandleWidth
* fHandleScale
) / 2;
1458 aCursorBottom
.y
= twipToPixel(rCursor
.y
, priv
->m_fZoom
) + twipToPixel(rCursor
.height
, priv
->m_fZoom
);
1460 cairo_save (pCairo
);
1461 cairo_scale(pCairo
, 1.0 / nScaleFactor
, 1.0 / nScaleFactor
);
1462 cairo_translate(pCairo
, aCursorBottom
.x
* nScaleFactor
, aCursorBottom
.y
* nScaleFactor
);
1463 cairo_scale(pCairo
, fHandleScale
* nScaleFactor
, fHandleScale
* nScaleFactor
);
1464 cairo_set_source_surface(pCairo
, pHandle
, 0, 0);
1465 cairo_paint(pCairo
);
1466 cairo_restore (pCairo
);
1468 rRectangle
.x
= aCursorBottom
.x
;
1469 rRectangle
.y
= aCursorBottom
.y
;
1470 rRectangle
.width
= nHandleWidth
* fHandleScale
;
1471 rRectangle
.height
= nHandleHeight
* fHandleScale
;
1474 /// Renders handles around an rSelection rectangle on pCairo.
1476 renderGraphicHandle(LOKDocView
* pDocView
,
1478 const GdkRectangle
& rSelection
,
1479 const GdkRGBA
& rColor
)
1481 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1482 int nHandleWidth
= 9, nHandleHeight
= 9;
1483 GdkRectangle aSelection
;
1485 aSelection
.x
= twipToPixel(rSelection
.x
, priv
->m_fZoom
);
1486 aSelection
.y
= twipToPixel(rSelection
.y
, priv
->m_fZoom
);
1487 aSelection
.width
= twipToPixel(rSelection
.width
, priv
->m_fZoom
);
1488 aSelection
.height
= twipToPixel(rSelection
.height
, priv
->m_fZoom
);
1490 for (int i
= 0; i
< GRAPHIC_HANDLE_COUNT
; ++i
)
1492 int x
= aSelection
.x
, y
= aSelection
.y
;
1498 case 1: // top-middle
1499 x
+= aSelection
.width
/ 2;
1501 case 2: // top-right
1502 x
+= aSelection
.width
;
1504 case 3: // middle-left
1505 y
+= aSelection
.height
/ 2;
1507 case 4: // middle-right
1508 x
+= aSelection
.width
;
1509 y
+= aSelection
.height
/ 2;
1511 case 5: // bottom-left
1512 y
+= aSelection
.height
;
1514 case 6: // bottom-middle
1515 x
+= aSelection
.width
/ 2;
1516 y
+= aSelection
.height
;
1518 case 7: // bottom-right
1519 x
+= aSelection
.width
;
1520 y
+= aSelection
.height
;
1524 // Center the handle.
1525 x
-= nHandleWidth
/ 2;
1526 y
-= nHandleHeight
/ 2;
1528 priv
->m_aGraphicHandleRects
[i
].x
= x
;
1529 priv
->m_aGraphicHandleRects
[i
].y
= y
;
1530 priv
->m_aGraphicHandleRects
[i
].width
= nHandleWidth
;
1531 priv
->m_aGraphicHandleRects
[i
].height
= nHandleHeight
;
1533 cairo_set_source_rgb(pCairo
, rColor
.red
, rColor
.green
, rColor
.blue
);
1534 cairo_rectangle(pCairo
, x
, y
, nHandleWidth
, nHandleHeight
);
1539 /// Finishes the paint tile operation and returns the result, if any
1541 paintTileFinish(LOKDocView
* pDocView
, GAsyncResult
* res
, GError
**error
)
1543 GTask
* task
= G_TASK(res
);
1545 g_return_val_if_fail(LOK_IS_DOC_VIEW(pDocView
), nullptr);
1546 g_return_val_if_fail(g_task_is_valid(res
, pDocView
), nullptr);
1547 g_return_val_if_fail(error
== nullptr || *error
== nullptr, nullptr);
1549 return g_task_propagate_pointer(task
, error
);
1552 /// Callback called in the main UI thread when paintTileInThread in LOK thread has finished
1554 paintTileCallback(GObject
* sourceObject
, GAsyncResult
* res
, gpointer userData
)
1556 LOKDocView
* pDocView
= LOK_DOC_VIEW(sourceObject
);
1557 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1558 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(userData
);
1559 std::unique_ptr
<TileBuffer
>& buffer
= priv
->m_pTileBuffer
;
1563 cairo_surface_t
* pSurface
= static_cast<cairo_surface_t
*>(paintTileFinish(pDocView
, res
, &error
));
1564 if (error
!= nullptr)
1566 if (error
->domain
== LOK_TILEBUFFER_ERROR
&&
1567 error
->code
== LOK_TILEBUFFER_CHANGED
)
1568 g_info("Skipping paint tile request because corresponding"
1569 "tile buffer has been destroyed");
1571 g_warning("Unable to get painted GdkPixbuf: %s", error
->message
);
1572 g_error_free(error
);
1576 buffer
->setTile(pLOEvent
->m_nPaintTileX
, pLOEvent
->m_nPaintTileY
, pSurface
);
1577 gdk_threads_add_idle(queueDraw
, GTK_WIDGET(pDocView
));
1579 cairo_surface_destroy(pSurface
);
1584 renderDocument(LOKDocView
* pDocView
, cairo_t
* pCairo
)
1586 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1587 GdkRectangle aVisibleArea
;
1588 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
1589 gint nTileSizePixelsScaled
= nTileSizePixels
* nScaleFactor
;
1590 long nDocumentWidthPixels
= twipToPixel(priv
->m_nDocumentWidthTwips
, priv
->m_fZoom
) * nScaleFactor
;
1591 long nDocumentHeightPixels
= twipToPixel(priv
->m_nDocumentHeightTwips
, priv
->m_fZoom
) * nScaleFactor
;
1592 // Total number of rows / columns in this document.
1593 guint nRows
= ceil(static_cast<double>(nDocumentHeightPixels
) / nTileSizePixelsScaled
);
1594 guint nColumns
= ceil(static_cast<double>(nDocumentWidthPixels
) / nTileSizePixelsScaled
);
1596 cairo_save (pCairo
);
1597 cairo_scale (pCairo
, 1.0/nScaleFactor
, 1.0/nScaleFactor
);
1598 gdk_cairo_get_clip_rectangle (pCairo
, &aVisibleArea
);
1599 aVisibleArea
.x
= pixelToTwip (aVisibleArea
.x
, priv
->m_fZoom
);
1600 aVisibleArea
.y
= pixelToTwip (aVisibleArea
.y
, priv
->m_fZoom
);
1601 aVisibleArea
.width
= pixelToTwip (aVisibleArea
.width
, priv
->m_fZoom
);
1602 aVisibleArea
.height
= pixelToTwip (aVisibleArea
.height
, priv
->m_fZoom
);
1604 // Render the tiles.
1605 for (guint nRow
= 0; nRow
< nRows
; ++nRow
)
1607 for (guint nColumn
= 0; nColumn
< nColumns
; ++nColumn
)
1609 GdkRectangle aTileRectangleTwips
, aTileRectanglePixels
;
1612 // Determine size of the tile: the rightmost/bottommost tiles may
1613 // be smaller, and we need the size to decide if we need to repaint.
1614 if (nColumn
== nColumns
- 1)
1615 aTileRectanglePixels
.width
= nDocumentWidthPixels
- nColumn
* nTileSizePixelsScaled
;
1617 aTileRectanglePixels
.width
= nTileSizePixelsScaled
;
1618 if (nRow
== nRows
- 1)
1619 aTileRectanglePixels
.height
= nDocumentHeightPixels
- nRow
* nTileSizePixelsScaled
;
1621 aTileRectanglePixels
.height
= nTileSizePixelsScaled
;
1623 // Determine size and position of the tile in document coordinates,
1624 // so we can decide if we can skip painting for partial rendering.
1625 aTileRectangleTwips
.x
= pixelToTwip(nTileSizePixelsScaled
, priv
->m_fZoom
) * nColumn
;
1626 aTileRectangleTwips
.y
= pixelToTwip(nTileSizePixelsScaled
, priv
->m_fZoom
) * nRow
;
1627 aTileRectangleTwips
.width
= pixelToTwip(aTileRectanglePixels
.width
, priv
->m_fZoom
);
1628 aTileRectangleTwips
.height
= pixelToTwip(aTileRectanglePixels
.height
, priv
->m_fZoom
);
1630 if (!gdk_rectangle_intersect(&aVisibleArea
, &aTileRectangleTwips
, nullptr))
1635 LOEvent
* pLOEvent
= new LOEvent(LOK_PAINT_TILE
);
1636 pLOEvent
->m_nPaintTileX
= nRow
;
1637 pLOEvent
->m_nPaintTileY
= nColumn
;
1638 pLOEvent
->m_fPaintTileZoom
= priv
->m_fZoom
;
1639 pLOEvent
->m_pTileBuffer
= &*priv
->m_pTileBuffer
;
1640 GTask
* task
= g_task_new(pDocView
, nullptr, paintTileCallback
, pLOEvent
);
1641 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
1643 Tile
& currentTile
= priv
->m_pTileBuffer
->getTile(nRow
, nColumn
, task
, priv
->lokThreadPool
);
1644 cairo_surface_t
* pSurface
= currentTile
.getBuffer();
1645 cairo_set_source_surface(pCairo
, pSurface
,
1646 twipToPixel(aTileRectangleTwips
.x
, priv
->m_fZoom
),
1647 twipToPixel(aTileRectangleTwips
.y
, priv
->m_fZoom
));
1648 cairo_paint(pCairo
);
1649 g_object_unref(task
);
1654 cairo_restore (pCairo
);
1658 static const GdkRGBA
& getDarkColor(int nViewId
, LOKDocViewPrivate
& priv
)
1660 static std::map
<int, GdkRGBA
> aColorMap
;
1661 auto it
= aColorMap
.find(nViewId
);
1662 if (it
!= aColorMap
.end())
1665 if (priv
->m_eDocumentType
== LOK_DOCTYPE_TEXT
)
1667 char* pValues
= priv
->m_pDocument
->pClass
->getCommandValues(priv
->m_pDocument
, ".uno:TrackedChangeAuthors");
1668 std::stringstream aInfo
;
1669 aInfo
<< "lok::Document::getCommandValues('.uno:TrackedChangeAuthors') returned '" << pValues
<< "'" << std::endl
;
1670 g_info("%s", aInfo
.str().c_str());
1672 std::stringstream
aStream(pValues
);
1673 boost::property_tree::ptree aTree
;
1674 boost::property_tree::read_json(aStream
, aTree
);
1675 for (const auto& rValue
: aTree
.get_child("authors"))
1677 const std::string
& rName
= rValue
.second
.get
<std::string
>("name");
1678 guint32 nColor
= rValue
.second
.get
<guint32
>("color");
1679 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};
1680 auto itAuthorViews
= g_aAuthorViews
.find(rName
);
1681 if (itAuthorViews
!= g_aAuthorViews
.end())
1682 aColorMap
[itAuthorViews
->second
] = aColor
;
1687 // Based on tools/color.hxx, COL_AUTHOR1_DARK..COL_AUTHOR9_DARK.
1688 static std::vector
<GdkRGBA
> aColors
=
1690 {(double(198))/255, (double(146))/255, (double(0))/255, 0},
1691 {(double(6))/255, (double(70))/255, (double(162))/255, 0},
1692 {(double(87))/255, (double(157))/255, (double(28))/255, 0},
1693 {(double(105))/255, (double(43))/255, (double(157))/255, 0},
1694 {(double(197))/255, (double(0))/255, (double(11))/255, 0},
1695 {(double(0))/255, (double(128))/255, (double(128))/255, 0},
1696 {(double(140))/255, (double(132))/255, (double(0))/255, 0},
1697 {(double(43))/255, (double(85))/255, (double(107))/255, 0},
1698 {(double(209))/255, (double(118))/255, (double(0))/255, 0},
1700 static int nColorCounter
= 0;
1701 GdkRGBA aColor
= aColors
[nColorCounter
++ % aColors
.size()];
1702 aColorMap
[nViewId
] = aColor
;
1704 assert(aColorMap
.find(nViewId
) != aColorMap
.end());
1705 return aColorMap
[nViewId
];
1709 renderOverlay(LOKDocView
* pDocView
, cairo_t
* pCairo
)
1711 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1713 if (priv
->m_bEdit
&& priv
->m_bCursorVisible
&& priv
->m_bCursorOverlayVisible
&& !isEmptyRectangle(priv
->m_aVisibleCursor
))
1715 if (priv
->m_aVisibleCursor
.width
< 30)
1716 // Set a minimal width if it would be 0.
1717 priv
->m_aVisibleCursor
.width
= 30;
1719 cairo_set_source_rgb(pCairo
, 0, 0, 0);
1720 cairo_rectangle(pCairo
,
1721 twipToPixel(priv
->m_aVisibleCursor
.x
, priv
->m_fZoom
),
1722 twipToPixel(priv
->m_aVisibleCursor
.y
, priv
->m_fZoom
),
1723 twipToPixel(priv
->m_aVisibleCursor
.width
, priv
->m_fZoom
),
1724 twipToPixel(priv
->m_aVisibleCursor
.height
, priv
->m_fZoom
));
1728 // View cursors: they do not blink and are colored.
1729 if (priv
->m_bEdit
&& !priv
->m_aViewCursors
.empty())
1731 for (auto& rPair
: priv
->m_aViewCursors
)
1733 auto itVisibility
= priv
->m_aViewCursorVisibilities
.find(rPair
.first
);
1734 if (itVisibility
!= priv
->m_aViewCursorVisibilities
.end() && !itVisibility
->second
)
1737 // Show view cursors when in Writer or when the part matches.
1738 if (rPair
.second
.m_nPart
!= priv
->m_nPartId
&& priv
->m_eDocumentType
!= LOK_DOCTYPE_TEXT
)
1741 GdkRectangle
& rCursor
= rPair
.second
.m_aRectangle
;
1742 if (rCursor
.width
< 30)
1743 // Set a minimal width if it would be 0.
1746 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
1747 cairo_set_source_rgb(pCairo
, rDark
.red
, rDark
.green
, rDark
.blue
);
1748 cairo_rectangle(pCairo
,
1749 twipToPixel(rCursor
.x
, priv
->m_fZoom
),
1750 twipToPixel(rCursor
.y
, priv
->m_fZoom
),
1751 twipToPixel(rCursor
.width
, priv
->m_fZoom
),
1752 twipToPixel(rCursor
.height
, priv
->m_fZoom
));
1757 if (priv
->m_bEdit
&& priv
->m_bCursorVisible
&& !isEmptyRectangle(priv
->m_aVisibleCursor
) && priv
->m_aTextSelectionRectangles
.empty())
1759 // Have a cursor, but no selection: we need the middle handle.
1760 gchar
* handleMiddlePath
= g_strconcat (priv
->m_aLOPath
.c_str(), CURSOR_HANDLE_DIR
, "handle_image_middle.png", nullptr);
1761 if (!priv
->m_pHandleMiddle
)
1763 priv
->m_pHandleMiddle
= cairo_image_surface_create_from_png(handleMiddlePath
);
1764 assert(cairo_surface_status(priv
->m_pHandleMiddle
) == CAIRO_STATUS_SUCCESS
);
1766 g_free (handleMiddlePath
);
1767 renderHandle(pDocView
, pCairo
, priv
->m_aVisibleCursor
, priv
->m_pHandleMiddle
, priv
->m_aHandleMiddleRect
);
1770 if (!priv
->m_aTextSelectionRectangles
.empty())
1772 for (const GdkRectangle
& rRectangle
: priv
->m_aTextSelectionRectangles
)
1774 // Blue with 75% transparency.
1775 cairo_set_source_rgba(pCairo
, (double(0x43))/255, (double(0xac))/255, (double(0xe8))/255, 0.25);
1776 cairo_rectangle(pCairo
,
1777 twipToPixel(rRectangle
.x
, priv
->m_fZoom
),
1778 twipToPixel(rRectangle
.y
, priv
->m_fZoom
),
1779 twipToPixel(rRectangle
.width
, priv
->m_fZoom
),
1780 twipToPixel(rRectangle
.height
, priv
->m_fZoom
));
1785 if (!isEmptyRectangle(priv
->m_aTextSelectionStart
))
1787 // Have a start position: we need a start handle.
1788 gchar
* handleStartPath
= g_strconcat (priv
->m_aLOPath
.c_str(), CURSOR_HANDLE_DIR
, "handle_image_start.png", nullptr);
1789 if (!priv
->m_pHandleStart
)
1791 priv
->m_pHandleStart
= cairo_image_surface_create_from_png(handleStartPath
);
1792 assert(cairo_surface_status(priv
->m_pHandleStart
) == CAIRO_STATUS_SUCCESS
);
1794 renderHandle(pDocView
, pCairo
, priv
->m_aTextSelectionStart
, priv
->m_pHandleStart
, priv
->m_aHandleStartRect
);
1795 g_free (handleStartPath
);
1797 if (!isEmptyRectangle(priv
->m_aTextSelectionEnd
))
1799 // Have a start position: we need an end handle.
1800 gchar
* handleEndPath
= g_strconcat (priv
->m_aLOPath
.c_str(), CURSOR_HANDLE_DIR
, "handle_image_end.png", nullptr);
1801 if (!priv
->m_pHandleEnd
)
1803 priv
->m_pHandleEnd
= cairo_image_surface_create_from_png(handleEndPath
);
1804 assert(cairo_surface_status(priv
->m_pHandleEnd
) == CAIRO_STATUS_SUCCESS
);
1806 renderHandle(pDocView
, pCairo
, priv
->m_aTextSelectionEnd
, priv
->m_pHandleEnd
, priv
->m_aHandleEndRect
);
1807 g_free (handleEndPath
);
1811 // Selections of other views.
1812 for (const auto& rPair
: priv
->m_aTextViewSelectionRectangles
)
1814 if (rPair
.second
.m_nPart
!= priv
->m_nPartId
&& priv
->m_eDocumentType
!= LOK_DOCTYPE_TEXT
)
1817 for (const GdkRectangle
& rRectangle
: rPair
.second
.m_aRectangles
)
1819 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
1820 // 75% transparency.
1821 cairo_set_source_rgba(pCairo
, rDark
.red
, rDark
.green
, rDark
.blue
, 0.25);
1822 cairo_rectangle(pCairo
,
1823 twipToPixel(rRectangle
.x
, priv
->m_fZoom
),
1824 twipToPixel(rRectangle
.y
, priv
->m_fZoom
),
1825 twipToPixel(rRectangle
.width
, priv
->m_fZoom
),
1826 twipToPixel(rRectangle
.height
, priv
->m_fZoom
));
1831 if (!isEmptyRectangle(priv
->m_aGraphicSelection
))
1833 GdkRGBA
const aBlack
{0, 0, 0, 0};
1834 renderGraphicHandle(pDocView
, pCairo
, priv
->m_aGraphicSelection
, aBlack
);
1837 // Graphic selections of other views.
1838 for (const auto& rPair
: priv
->m_aGraphicViewSelections
)
1840 const ViewRectangle
& rRectangle
= rPair
.second
;
1841 if (rRectangle
.m_nPart
!= priv
->m_nPartId
&& priv
->m_eDocumentType
!= LOK_DOCTYPE_TEXT
)
1844 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
1845 renderGraphicHandle(pDocView
, pCairo
, rRectangle
.m_aRectangle
, rDark
);
1848 // Draw the cell cursor.
1849 if (!isEmptyRectangle(priv
->m_aCellCursor
))
1851 cairo_set_source_rgb(pCairo
, 0, 0, 0);
1852 cairo_rectangle(pCairo
,
1853 twipToPixel(priv
->m_aCellCursor
.x
, priv
->m_fZoom
),
1854 twipToPixel(priv
->m_aCellCursor
.y
, priv
->m_fZoom
),
1855 twipToPixel(priv
->m_aCellCursor
.width
, priv
->m_fZoom
),
1856 twipToPixel(priv
->m_aCellCursor
.height
, priv
->m_fZoom
));
1857 cairo_set_line_width(pCairo
, 2.0);
1858 cairo_stroke(pCairo
);
1861 // Cell view cursors: they are colored.
1862 for (const auto& rPair
: priv
->m_aCellViewCursors
)
1864 const ViewRectangle
& rCursor
= rPair
.second
;
1865 if (rCursor
.m_nPart
!= priv
->m_nPartId
)
1868 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
1869 cairo_set_source_rgb(pCairo
, rDark
.red
, rDark
.green
, rDark
.blue
);
1870 cairo_rectangle(pCairo
,
1871 twipToPixel(rCursor
.m_aRectangle
.x
, priv
->m_fZoom
),
1872 twipToPixel(rCursor
.m_aRectangle
.y
, priv
->m_fZoom
),
1873 twipToPixel(rCursor
.m_aRectangle
.width
, priv
->m_fZoom
),
1874 twipToPixel(rCursor
.m_aRectangle
.height
, priv
->m_fZoom
));
1875 cairo_set_line_width(pCairo
, 2.0);
1876 cairo_stroke(pCairo
);
1879 // Draw reference marks.
1880 for (const auto& rPair
: priv
->m_aReferenceMarks
)
1882 const ViewRectangle
& rMark
= rPair
.first
;
1883 if (rMark
.m_nPart
!= priv
->m_nPartId
)
1886 sal_uInt32 nColor
= rPair
.second
;
1887 sal_uInt8 nRed
= (nColor
>> 16) & 0xff;
1888 sal_uInt8 nGreen
= (nColor
>> 8) & 0xff;
1889 sal_uInt8 nBlue
= nColor
& 0xff;
1890 cairo_set_source_rgb(pCairo
, nRed
, nGreen
, nBlue
);
1891 cairo_rectangle(pCairo
,
1892 twipToPixel(rMark
.m_aRectangle
.x
, priv
->m_fZoom
),
1893 twipToPixel(rMark
.m_aRectangle
.y
, priv
->m_fZoom
),
1894 twipToPixel(rMark
.m_aRectangle
.width
, priv
->m_fZoom
),
1895 twipToPixel(rMark
.m_aRectangle
.height
, priv
->m_fZoom
));
1896 cairo_set_line_width(pCairo
, 2.0);
1897 cairo_stroke(pCairo
);
1900 // View locks: they are colored.
1901 for (const auto& rPair
: priv
->m_aViewLockRectangles
)
1903 const ViewRectangle
& rRectangle
= rPair
.second
;
1904 if (rRectangle
.m_nPart
!= priv
->m_nPartId
)
1907 // Draw a rectangle.
1908 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
1909 cairo_set_source_rgb(pCairo
, rDark
.red
, rDark
.green
, rDark
.blue
);
1910 cairo_rectangle(pCairo
,
1911 twipToPixel(rRectangle
.m_aRectangle
.x
, priv
->m_fZoom
),
1912 twipToPixel(rRectangle
.m_aRectangle
.y
, priv
->m_fZoom
),
1913 twipToPixel(rRectangle
.m_aRectangle
.width
, priv
->m_fZoom
),
1914 twipToPixel(rRectangle
.m_aRectangle
.height
, priv
->m_fZoom
));
1915 cairo_set_line_width(pCairo
, 2.0);
1916 cairo_stroke(pCairo
);
1919 cairo_rectangle(pCairo
,
1920 twipToPixel(rRectangle
.m_aRectangle
.x
+ rRectangle
.m_aRectangle
.width
, priv
->m_fZoom
) - 25,
1921 twipToPixel(rRectangle
.m_aRectangle
.y
+ rRectangle
.m_aRectangle
.height
, priv
->m_fZoom
) - 15,
1926 twipToPixel(rRectangle
.m_aRectangle
.x
+ rRectangle
.m_aRectangle
.width
, priv
->m_fZoom
) - 15,
1927 twipToPixel(rRectangle
.m_aRectangle
.y
+ rRectangle
.m_aRectangle
.height
, priv
->m_fZoom
) - 15,
1929 180.0 * (M_PI
/180.0),
1930 360.0 * (M_PI
/180.0));
1931 cairo_stroke(pCairo
);
1938 lok_doc_view_signal_button(GtkWidget
* pWidget
, GdkEventButton
* pEvent
)
1940 LOKDocView
* pDocView
= LOK_DOC_VIEW (pWidget
);
1941 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1942 GError
* error
= nullptr;
1944 g_info("LOKDocView_Impl::signalButton: %d, %d (in twips: %d, %d)",
1945 static_cast<int>(pEvent
->x
), static_cast<int>(pEvent
->y
),
1946 static_cast<int>(pixelToTwip(pEvent
->x
, priv
->m_fZoom
)),
1947 static_cast<int>(pixelToTwip(pEvent
->y
, priv
->m_fZoom
)));
1948 gtk_widget_grab_focus(GTK_WIDGET(pDocView
));
1950 switch (pEvent
->type
)
1952 case GDK_BUTTON_PRESS
:
1954 GdkRectangle aClick
;
1955 aClick
.x
= pEvent
->x
;
1956 aClick
.y
= pEvent
->y
;
1960 if (handleTextSelectionOnButtonPress(aClick
, pDocView
))
1962 if (handleGraphicSelectionOnButtonPress(aClick
, pDocView
))
1966 if ((pEvent
->time
- priv
->m_nLastButtonPressTime
) < 250)
1968 priv
->m_nLastButtonPressTime
= pEvent
->time
;
1969 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
1970 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_MOUSE_EVENT
);
1971 pLOEvent
->m_nPostMouseEventType
= LOK_MOUSEEVENT_MOUSEBUTTONDOWN
;
1972 pLOEvent
->m_nPostMouseEventX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
1973 pLOEvent
->m_nPostMouseEventY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
1974 pLOEvent
->m_nPostMouseEventCount
= nCount
;
1975 switch (pEvent
->button
)
1978 pLOEvent
->m_nPostMouseEventButton
= MOUSE_LEFT
;
1981 pLOEvent
->m_nPostMouseEventButton
= MOUSE_MIDDLE
;
1984 pLOEvent
->m_nPostMouseEventButton
= MOUSE_RIGHT
;
1987 pLOEvent
->m_nPostMouseEventModifier
= priv
->m_nKeyModifier
;
1988 priv
->m_nLastButtonPressed
= pLOEvent
->m_nPostMouseEventButton
;
1989 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
1991 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
1992 if (error
!= nullptr)
1994 g_warning("Unable to call LOK_POST_MOUSE_EVENT: %s", error
->message
);
1995 g_clear_error(&error
);
1997 g_object_unref(task
);
2000 case GDK_BUTTON_RELEASE
:
2002 if (handleTextSelectionOnButtonRelease(pDocView
))
2004 if (handleGraphicSelectionOnButtonRelease(pDocView
, pEvent
))
2008 if ((pEvent
->time
- priv
->m_nLastButtonReleaseTime
) < 250)
2010 priv
->m_nLastButtonReleaseTime
= pEvent
->time
;
2011 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
2012 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_MOUSE_EVENT
);
2013 pLOEvent
->m_nPostMouseEventType
= LOK_MOUSEEVENT_MOUSEBUTTONUP
;
2014 pLOEvent
->m_nPostMouseEventX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
2015 pLOEvent
->m_nPostMouseEventY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
2016 pLOEvent
->m_nPostMouseEventCount
= nCount
;
2017 switch (pEvent
->button
)
2020 pLOEvent
->m_nPostMouseEventButton
= MOUSE_LEFT
;
2023 pLOEvent
->m_nPostMouseEventButton
= MOUSE_MIDDLE
;
2026 pLOEvent
->m_nPostMouseEventButton
= MOUSE_RIGHT
;
2029 pLOEvent
->m_nPostMouseEventModifier
= priv
->m_nKeyModifier
;
2030 priv
->m_nLastButtonPressed
= pLOEvent
->m_nPostMouseEventButton
;
2031 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
2033 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
2034 if (error
!= nullptr)
2036 g_warning("Unable to call LOK_POST_MOUSE_EVENT: %s", error
->message
);
2037 g_clear_error(&error
);
2039 g_object_unref(task
);
2049 getDragPoint(GdkRectangle
* pHandle
,
2050 GdkEventMotion
* pEvent
,
2053 GdkPoint aCursor
, aHandle
;
2055 // Center of the cursor rectangle: we know that it's above the handle.
2056 aCursor
.x
= pHandle
->x
+ pHandle
->width
/ 2;
2057 aCursor
.y
= pHandle
->y
- pHandle
->height
/ 2;
2058 // Center of the handle rectangle.
2059 aHandle
.x
= pHandle
->x
+ pHandle
->width
/ 2;
2060 aHandle
.y
= pHandle
->y
+ pHandle
->height
/ 2;
2061 // Our target is the original cursor position + the dragged offset.
2062 pPoint
->x
= aCursor
.x
+ (pEvent
->x
- aHandle
.x
);
2063 pPoint
->y
= aCursor
.y
+ (pEvent
->y
- aHandle
.y
);
2067 lok_doc_view_signal_motion (GtkWidget
* pWidget
, GdkEventMotion
* pEvent
)
2069 LOKDocView
* pDocView
= LOK_DOC_VIEW (pWidget
);
2070 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2072 GError
* error
= nullptr;
2074 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2075 std::stringstream ss
;
2076 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2077 g_info("%s", ss
.str().c_str());
2078 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2079 if (priv
->m_bInDragMiddleHandle
)
2081 g_info("lcl_signalMotion: dragging the middle handle");
2082 getDragPoint(&priv
->m_aHandleMiddleRect
, pEvent
, &aPoint
);
2083 priv
->m_pDocument
->pClass
->setTextSelection(priv
->m_pDocument
, LOK_SETTEXTSELECTION_RESET
, pixelToTwip(aPoint
.x
, priv
->m_fZoom
), pixelToTwip(aPoint
.y
, priv
->m_fZoom
));
2086 if (priv
->m_bInDragStartHandle
)
2088 g_info("lcl_signalMotion: dragging the start handle");
2089 getDragPoint(&priv
->m_aHandleStartRect
, pEvent
, &aPoint
);
2090 priv
->m_pDocument
->pClass
->setTextSelection(priv
->m_pDocument
, LOK_SETTEXTSELECTION_START
, pixelToTwip(aPoint
.x
, priv
->m_fZoom
), pixelToTwip(aPoint
.y
, priv
->m_fZoom
));
2093 if (priv
->m_bInDragEndHandle
)
2095 g_info("lcl_signalMotion: dragging the end handle");
2096 getDragPoint(&priv
->m_aHandleEndRect
, pEvent
, &aPoint
);
2097 priv
->m_pDocument
->pClass
->setTextSelection(priv
->m_pDocument
, LOK_SETTEXTSELECTION_END
, pixelToTwip(aPoint
.x
, priv
->m_fZoom
), pixelToTwip(aPoint
.y
, priv
->m_fZoom
));
2101 for (int i
= 0; i
< GRAPHIC_HANDLE_COUNT
; ++i
)
2103 if (priv
->m_bInDragGraphicHandles
[i
])
2105 g_info("lcl_signalMotion: dragging the graphic handle #%d", i
);
2109 if (priv
->m_bInDragGraphicSelection
)
2111 g_info("lcl_signalMotion: dragging the graphic selection");
2115 GdkRectangle aMotionInTwipsInTwips
;
2116 aMotionInTwipsInTwips
.x
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
2117 aMotionInTwipsInTwips
.y
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
2118 aMotionInTwipsInTwips
.width
= 1;
2119 aMotionInTwipsInTwips
.height
= 1;
2120 if (gdk_rectangle_intersect(&aMotionInTwipsInTwips
, &priv
->m_aGraphicSelection
, nullptr))
2122 g_info("lcl_signalMotion: start of drag graphic selection");
2123 priv
->m_bInDragGraphicSelection
= true;
2125 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
2126 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_GRAPHIC_SELECTION
);
2127 pLOEvent
->m_nSetGraphicSelectionType
= LOK_SETGRAPHICSELECTION_START
;
2128 pLOEvent
->m_nSetGraphicSelectionX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
2129 pLOEvent
->m_nSetGraphicSelectionY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
2130 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
2132 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
2133 if (error
!= nullptr)
2135 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error
->message
);
2136 g_clear_error(&error
);
2138 g_object_unref(task
);
2143 // Otherwise a mouse move, as on the desktop.
2145 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
2146 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_MOUSE_EVENT
);
2147 pLOEvent
->m_nPostMouseEventType
= LOK_MOUSEEVENT_MOUSEMOVE
;
2148 pLOEvent
->m_nPostMouseEventX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
2149 pLOEvent
->m_nPostMouseEventY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
2150 pLOEvent
->m_nPostMouseEventCount
= 1;
2151 pLOEvent
->m_nPostMouseEventButton
= priv
->m_nLastButtonPressed
;
2152 pLOEvent
->m_nPostMouseEventModifier
= priv
->m_nKeyModifier
;
2154 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
2156 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
2157 if (error
!= nullptr)
2159 g_warning("Unable to call LOK_MOUSEEVENT_MOUSEMOVE: %s", error
->message
);
2160 g_clear_error(&error
);
2162 g_object_unref(task
);
2168 setGraphicSelectionInThread(gpointer data
)
2170 GTask
* task
= G_TASK(data
);
2171 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2172 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2173 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2175 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2176 std::stringstream ss
;
2177 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2178 g_info("%s", ss
.str().c_str());
2179 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2180 ss
.str(std::string());
2181 ss
<< "lok::Document::setGraphicSelection(" << pLOEvent
->m_nSetGraphicSelectionType
;
2182 ss
<< ", " << pLOEvent
->m_nSetGraphicSelectionX
;
2183 ss
<< ", " << pLOEvent
->m_nSetGraphicSelectionY
<< ")";
2184 g_info("%s", ss
.str().c_str());
2185 priv
->m_pDocument
->pClass
->setGraphicSelection(priv
->m_pDocument
,
2186 pLOEvent
->m_nSetGraphicSelectionType
,
2187 pLOEvent
->m_nSetGraphicSelectionX
,
2188 pLOEvent
->m_nSetGraphicSelectionY
);
2192 setClientZoomInThread(gpointer data
)
2194 GTask
* task
= G_TASK(data
);
2195 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2196 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2197 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2199 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2200 priv
->m_pDocument
->pClass
->setClientZoom(priv
->m_pDocument
,
2201 pLOEvent
->m_nTilePixelWidth
,
2202 pLOEvent
->m_nTilePixelHeight
,
2203 pLOEvent
->m_nTileTwipWidth
,
2204 pLOEvent
->m_nTileTwipHeight
);
2208 postMouseEventInThread(gpointer data
)
2210 GTask
* task
= G_TASK(data
);
2211 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2212 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2213 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2215 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2216 std::stringstream ss
;
2217 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2218 g_info("%s", ss
.str().c_str());
2219 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2220 ss
.str(std::string());
2221 ss
<< "lok::Document::postMouseEvent(" << pLOEvent
->m_nPostMouseEventType
;
2222 ss
<< ", " << pLOEvent
->m_nPostMouseEventX
;
2223 ss
<< ", " << pLOEvent
->m_nPostMouseEventY
;
2224 ss
<< ", " << pLOEvent
->m_nPostMouseEventCount
;
2225 ss
<< ", " << pLOEvent
->m_nPostMouseEventButton
;
2226 ss
<< ", " << pLOEvent
->m_nPostMouseEventModifier
<< ")";
2227 g_info("%s", ss
.str().c_str());
2228 priv
->m_pDocument
->pClass
->postMouseEvent(priv
->m_pDocument
,
2229 pLOEvent
->m_nPostMouseEventType
,
2230 pLOEvent
->m_nPostMouseEventX
,
2231 pLOEvent
->m_nPostMouseEventY
,
2232 pLOEvent
->m_nPostMouseEventCount
,
2233 pLOEvent
->m_nPostMouseEventButton
,
2234 pLOEvent
->m_nPostMouseEventModifier
);
2238 openDocumentInThread (gpointer data
)
2240 GTask
* task
= G_TASK(data
);
2241 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2242 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2244 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2245 if ( priv
->m_pDocument
)
2247 priv
->m_pDocument
->pClass
->destroy( priv
->m_pDocument
);
2248 priv
->m_pDocument
= nullptr;
2251 priv
->m_pOffice
->pClass
->registerCallback(priv
->m_pOffice
, globalCallbackWorker
, pDocView
);
2252 priv
->m_pDocument
= priv
->m_pOffice
->pClass
->documentLoadWithOptions( priv
->m_pOffice
, priv
->m_aDocPath
.c_str(), "en-US" );
2253 if ( !priv
->m_pDocument
)
2255 char *pError
= priv
->m_pOffice
->pClass
->getError( priv
->m_pOffice
);
2256 g_task_return_new_error(task
, g_quark_from_static_string ("LOK error"), 0, "%s", pError
);
2260 priv
->m_eDocumentType
= static_cast<LibreOfficeKitDocumentType
>(priv
->m_pDocument
->pClass
->getDocumentType(priv
->m_pDocument
));
2261 gdk_threads_add_idle(postDocumentLoad
, pDocView
);
2262 g_task_return_boolean (task
, true);
2267 setPartInThread(gpointer data
)
2269 GTask
* task
= G_TASK(data
);
2270 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2271 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2272 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2273 int nPart
= pLOEvent
->m_nPart
;
2275 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2276 std::stringstream ss
;
2277 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2278 g_info("%s", ss
.str().c_str());
2279 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2280 priv
->m_pDocument
->pClass
->setPart( priv
->m_pDocument
, nPart
);
2283 lok_doc_view_reset_view(pDocView
);
2287 setPartmodeInThread(gpointer data
)
2289 GTask
* task
= G_TASK(data
);
2290 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2291 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2292 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2293 int nPartMode
= pLOEvent
->m_nPartMode
;
2295 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2296 std::stringstream ss
;
2297 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2298 g_info("%s", ss
.str().c_str());
2299 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2300 priv
->m_pDocument
->pClass
->setPartMode( priv
->m_pDocument
, nPartMode
);
2304 setEditInThread(gpointer data
)
2306 GTask
* task
= G_TASK(data
);
2307 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2308 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2309 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2310 bool bWasEdit
= priv
->m_bEdit
;
2311 bool bEdit
= pLOEvent
->m_bEdit
;
2313 if (!priv
->m_bEdit
&& bEdit
)
2314 g_info("lok_doc_view_set_edit: entering edit mode");
2315 else if (priv
->m_bEdit
&& !bEdit
)
2317 g_info("lok_doc_view_set_edit: leaving edit mode");
2318 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2319 std::stringstream ss
;
2320 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2321 g_info("%s", ss
.str().c_str());
2322 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2323 priv
->m_pDocument
->pClass
->resetSelection(priv
->m_pDocument
);
2325 priv
->m_bEdit
= bEdit
;
2326 g_signal_emit(pDocView
, doc_view_signals
[EDIT_CHANGED
], 0, bWasEdit
);
2327 gdk_threads_add_idle(queueDraw
, GTK_WIDGET(pDocView
));
2331 postCommandInThread (gpointer data
)
2333 GTask
* task
= G_TASK(data
);
2334 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2335 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2336 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2338 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2339 std::stringstream ss
;
2340 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2341 g_info("%s", ss
.str().c_str());
2342 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2343 ss
.str(std::string());
2344 ss
<< "lok::Document::postUnoCommand(" << pLOEvent
->m_pCommand
<< ", " << pLOEvent
->m_pArguments
<< ")";
2345 g_info("%s", ss
.str().c_str());
2346 priv
->m_pDocument
->pClass
->postUnoCommand(priv
->m_pDocument
, pLOEvent
->m_pCommand
, pLOEvent
->m_pArguments
, pLOEvent
->m_bNotifyWhenFinished
);
2350 paintTileInThread (gpointer data
)
2352 GTask
* task
= G_TASK(data
);
2353 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2354 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2355 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2356 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
2357 gint nTileSizePixelsScaled
= nTileSizePixels
* nScaleFactor
;
2359 // check if "source" tile buffer is different from "current" tile buffer
2360 if (pLOEvent
->m_pTileBuffer
!= &*priv
->m_pTileBuffer
)
2362 pLOEvent
->m_pTileBuffer
= nullptr;
2363 g_task_return_new_error(task
,
2364 LOK_TILEBUFFER_ERROR
,
2365 LOK_TILEBUFFER_CHANGED
,
2366 "TileBuffer has changed");
2369 std::unique_ptr
<TileBuffer
>& buffer
= priv
->m_pTileBuffer
;
2370 if (buffer
->hasValidTile(pLOEvent
->m_nPaintTileX
, pLOEvent
->m_nPaintTileY
))
2373 cairo_surface_t
*pSurface
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32
, nTileSizePixelsScaled
, nTileSizePixelsScaled
);
2374 if (cairo_surface_status(pSurface
) != CAIRO_STATUS_SUCCESS
)
2376 cairo_surface_destroy(pSurface
);
2377 g_task_return_new_error(task
,
2378 LOK_TILEBUFFER_ERROR
,
2379 LOK_TILEBUFFER_MEMORY
,
2380 "Error allocating Surface");
2384 unsigned char* pBuffer
= cairo_image_surface_get_data(pSurface
);
2385 GdkRectangle aTileRectangle
;
2386 aTileRectangle
.x
= pixelToTwip(nTileSizePixelsScaled
, pLOEvent
->m_fPaintTileZoom
* nScaleFactor
) * pLOEvent
->m_nPaintTileY
;
2387 aTileRectangle
.y
= pixelToTwip(nTileSizePixelsScaled
, pLOEvent
->m_fPaintTileZoom
* nScaleFactor
) * pLOEvent
->m_nPaintTileX
;
2389 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2390 std::stringstream ss
;
2391 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2392 g_info("%s", ss
.str().c_str());
2393 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2394 ss
.str(std::string());
2395 GTimer
* aTimer
= g_timer_new();
2397 ss
<< "lok::Document::paintTile(" << static_cast<void*>(pBuffer
) << ", "
2398 << nTileSizePixelsScaled
<< ", " << nTileSizePixelsScaled
<< ", "
2399 << aTileRectangle
.x
<< ", " << aTileRectangle
.y
<< ", "
2400 << pixelToTwip(nTileSizePixelsScaled
, pLOEvent
->m_fPaintTileZoom
* nScaleFactor
) << ", "
2401 << pixelToTwip(nTileSizePixelsScaled
, pLOEvent
->m_fPaintTileZoom
* nScaleFactor
) << ")";
2403 priv
->m_pDocument
->pClass
->paintTile(priv
->m_pDocument
,
2405 nTileSizePixelsScaled
, nTileSizePixelsScaled
,
2406 aTileRectangle
.x
, aTileRectangle
.y
,
2407 pixelToTwip(nTileSizePixelsScaled
, pLOEvent
->m_fPaintTileZoom
* nScaleFactor
),
2408 pixelToTwip(nTileSizePixelsScaled
, pLOEvent
->m_fPaintTileZoom
* nScaleFactor
));
2411 g_timer_elapsed(aTimer
, &nElapsedMs
);
2412 ss
<< " rendered in " << (nElapsedMs
/ 1000.) << " milliseconds";
2413 g_info("%s", ss
.str().c_str());
2414 g_timer_destroy(aTimer
);
2416 cairo_surface_mark_dirty(pSurface
);
2418 // Its likely that while the tilebuffer has changed, one of the paint tile
2419 // requests has passed the previous check at start of this function, and has
2420 // rendered the tile already. We want to stop such rendered tiles from being
2421 // stored in new tile buffer.
2422 if (pLOEvent
->m_pTileBuffer
!= &*priv
->m_pTileBuffer
)
2424 pLOEvent
->m_pTileBuffer
= nullptr;
2425 g_task_return_new_error(task
,
2426 LOK_TILEBUFFER_ERROR
,
2427 LOK_TILEBUFFER_CHANGED
,
2428 "TileBuffer has changed");
2432 g_task_return_pointer(task
, pSurface
, reinterpret_cast<GDestroyNotify
>(cairo_surface_destroy
));
2437 lokThreadFunc(gpointer data
, gpointer
/*user_data*/)
2439 GTask
* task
= G_TASK(data
);
2440 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2441 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2442 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2444 switch (pLOEvent
->m_nType
)
2447 openDocumentInThread(task
);
2449 case LOK_POST_COMMAND
:
2450 postCommandInThread(task
);
2453 setEditInThread(task
);
2456 setPartInThread(task
);
2458 case LOK_SET_PARTMODE
:
2459 setPartmodeInThread(task
);
2462 // view-only/editable mode already checked during signal key signal emission
2463 postKeyEventInThread(task
);
2465 case LOK_PAINT_TILE
:
2466 paintTileInThread(task
);
2468 case LOK_POST_MOUSE_EVENT
:
2469 postMouseEventInThread(task
);
2471 case LOK_SET_GRAPHIC_SELECTION
:
2473 setGraphicSelectionInThread(task
);
2475 g_info ("LOK_SET_GRAPHIC_SELECTION: skipping graphical operation in view-only mode");
2477 case LOK_SET_CLIENT_ZOOM
:
2478 setClientZoomInThread(task
);
2482 g_object_unref(task
);
2486 onStyleContextChanged (LOKDocView
* pDocView
)
2488 // The scale factor might have changed
2489 updateClientZoom (pDocView
);
2490 gtk_widget_queue_draw (GTK_WIDGET (pDocView
));
2493 static void lok_doc_view_init (LOKDocView
* pDocView
)
2495 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2496 priv
.m_pImpl
= new LOKDocViewPrivateImpl();
2498 gtk_widget_add_events(GTK_WIDGET(pDocView
),
2499 GDK_BUTTON_PRESS_MASK
2500 |GDK_BUTTON_RELEASE_MASK
2501 |GDK_BUTTON_MOTION_MASK
2503 |GDK_KEY_RELEASE_MASK
);
2505 priv
->lokThreadPool
= g_thread_pool_new(lokThreadFunc
,
2511 g_signal_connect (pDocView
, "style-updated", G_CALLBACK(onStyleContextChanged
), nullptr);
2514 static void lok_doc_view_set_property (GObject
* object
, guint propId
, const GValue
*value
, GParamSpec
*pspec
)
2516 LOKDocView
* pDocView
= LOK_DOC_VIEW (object
);
2517 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2518 bool bDocPasswordEnabled
= priv
->m_nLOKFeatures
& LOK_FEATURE_DOCUMENT_PASSWORD
;
2519 bool bDocPasswordToModifyEnabled
= priv
->m_nLOKFeatures
& LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY
;
2520 bool bTiledAnnotationsEnabled
= !(priv
->m_nLOKFeatures
& LOK_FEATURE_NO_TILED_ANNOTATIONS
);
2525 priv
->m_aLOPath
= g_value_get_string (value
);
2527 case PROP_LO_UNIPOLL
:
2528 priv
->m_bUnipoll
= g_value_get_boolean (value
);
2530 case PROP_LO_POINTER
:
2531 priv
->m_pOffice
= static_cast<LibreOfficeKit
*>(g_value_get_pointer(value
));
2533 case PROP_USER_PROFILE_URL
:
2534 if (const gchar
* pUserProfile
= g_value_get_string(value
))
2535 priv
->m_aUserProfileURL
= pUserProfile
;
2538 priv
->m_aDocPath
= g_value_get_string (value
);
2540 case PROP_DOC_POINTER
:
2541 priv
->m_pDocument
= static_cast<LibreOfficeKitDocument
*>(g_value_get_pointer(value
));
2542 priv
->m_eDocumentType
= static_cast<LibreOfficeKitDocumentType
>(priv
->m_pDocument
->pClass
->getDocumentType(priv
->m_pDocument
));
2545 lok_doc_view_set_edit (pDocView
, g_value_get_boolean (value
));
2548 lok_doc_view_set_zoom (pDocView
, g_value_get_float (value
));
2550 case PROP_DOC_WIDTH
:
2551 priv
->m_nDocumentWidthTwips
= g_value_get_long (value
);
2553 case PROP_DOC_HEIGHT
:
2554 priv
->m_nDocumentHeightTwips
= g_value_get_long (value
);
2556 case PROP_DOC_PASSWORD
:
2557 if (bool(g_value_get_boolean (value
)) != bDocPasswordEnabled
)
2559 priv
->m_nLOKFeatures
= priv
->m_nLOKFeatures
^ LOK_FEATURE_DOCUMENT_PASSWORD
;
2560 priv
->m_pOffice
->pClass
->setOptionalFeatures(priv
->m_pOffice
, priv
->m_nLOKFeatures
);
2563 case PROP_DOC_PASSWORD_TO_MODIFY
:
2564 if ( bool(g_value_get_boolean (value
)) != bDocPasswordToModifyEnabled
)
2566 priv
->m_nLOKFeatures
= priv
->m_nLOKFeatures
^ LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY
;
2567 priv
->m_pOffice
->pClass
->setOptionalFeatures(priv
->m_pOffice
, priv
->m_nLOKFeatures
);
2570 case PROP_TILED_ANNOTATIONS
:
2571 if ( bool(g_value_get_boolean (value
)) != bTiledAnnotationsEnabled
)
2573 priv
->m_nLOKFeatures
= priv
->m_nLOKFeatures
^ LOK_FEATURE_NO_TILED_ANNOTATIONS
;
2574 priv
->m_pOffice
->pClass
->setOptionalFeatures(priv
->m_pOffice
, priv
->m_nLOKFeatures
);
2578 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, propId
, pspec
);
2582 static void lok_doc_view_get_property (GObject
* object
, guint propId
, GValue
*value
, GParamSpec
*pspec
)
2584 LOKDocView
* pDocView
= LOK_DOC_VIEW (object
);
2585 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2590 g_value_set_string (value
, priv
->m_aLOPath
.c_str());
2592 case PROP_LO_UNIPOLL
:
2593 g_value_set_boolean (value
, priv
->m_bUnipoll
);
2595 case PROP_LO_POINTER
:
2596 g_value_set_pointer(value
, priv
->m_pOffice
);
2598 case PROP_USER_PROFILE_URL
:
2599 g_value_set_string(value
, priv
->m_aUserProfileURL
.c_str());
2602 g_value_set_string (value
, priv
->m_aDocPath
.c_str());
2604 case PROP_DOC_POINTER
:
2605 g_value_set_pointer(value
, priv
->m_pDocument
);
2608 g_value_set_boolean (value
, priv
->m_bEdit
);
2610 case PROP_LOAD_PROGRESS
:
2611 g_value_set_double (value
, priv
->m_nLoadProgress
);
2614 g_value_set_float (value
, priv
->m_fZoom
);
2616 case PROP_IS_LOADING
:
2617 g_value_set_boolean (value
, priv
->m_bIsLoading
);
2619 case PROP_IS_INITIALIZED
:
2620 g_value_set_boolean (value
, priv
->m_bInit
);
2622 case PROP_DOC_WIDTH
:
2623 g_value_set_long (value
, priv
->m_nDocumentWidthTwips
);
2625 case PROP_DOC_HEIGHT
:
2626 g_value_set_long (value
, priv
->m_nDocumentHeightTwips
);
2628 case PROP_CAN_ZOOM_IN
:
2629 g_value_set_boolean (value
, priv
->m_bCanZoomIn
);
2631 case PROP_CAN_ZOOM_OUT
:
2632 g_value_set_boolean (value
, priv
->m_bCanZoomOut
);
2634 case PROP_DOC_PASSWORD
:
2635 g_value_set_boolean (value
, (priv
->m_nLOKFeatures
& LOK_FEATURE_DOCUMENT_PASSWORD
) != 0);
2637 case PROP_DOC_PASSWORD_TO_MODIFY
:
2638 g_value_set_boolean (value
, (priv
->m_nLOKFeatures
& LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY
) != 0);
2640 case PROP_TILED_ANNOTATIONS
:
2641 g_value_set_boolean (value
, !(priv
->m_nLOKFeatures
& LOK_FEATURE_NO_TILED_ANNOTATIONS
));
2644 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, propId
, pspec
);
2648 static gboolean
lok_doc_view_draw (GtkWidget
* pWidget
, cairo_t
* pCairo
)
2650 LOKDocView
*pDocView
= LOK_DOC_VIEW (pWidget
);
2652 renderDocument (pDocView
, pCairo
);
2653 renderOverlay (pDocView
, pCairo
);
2658 //rhbz#1444437 finalize may not occur immediately when this widget is destroyed
2659 //it may happen during GC of javascript, e.g. in gnome-documents but "destroy"
2660 //will be called promptly, so close documents in destroy, not finalize
2661 static void lok_doc_view_destroy (GtkWidget
* widget
)
2663 LOKDocView
* pDocView
= LOK_DOC_VIEW (widget
);
2664 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2666 // Ignore notifications sent to this view on shutdown.
2667 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2668 std::stringstream ss
;
2669 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2670 g_info("%s", ss
.str().c_str());
2671 if (priv
->m_pDocument
)
2673 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2674 priv
->m_pDocument
->pClass
->registerCallback(priv
->m_pDocument
, nullptr, nullptr);
2677 if (priv
->lokThreadPool
)
2679 g_thread_pool_free(priv
->lokThreadPool
, true, true);
2680 priv
->lokThreadPool
= nullptr;
2685 if (priv
->m_pDocument
)
2687 if (priv
->m_pDocument
->pClass
->getViewsCount(priv
->m_pDocument
) > 1)
2689 priv
->m_pDocument
->pClass
->destroyView(priv
->m_pDocument
, priv
->m_nViewId
);
2693 if (priv
->m_pDocument
)
2695 priv
->m_pDocument
->pClass
->destroy (priv
->m_pDocument
);
2696 priv
->m_pDocument
= nullptr;
2698 if (priv
->m_pOffice
)
2700 priv
->m_pOffice
->pClass
->destroy (priv
->m_pOffice
);
2701 priv
->m_pOffice
= nullptr;
2706 GTK_WIDGET_CLASS (lok_doc_view_parent_class
)->destroy (widget
);
2709 static void lok_doc_view_finalize (GObject
* object
)
2711 LOKDocView
* pDocView
= LOK_DOC_VIEW (object
);
2712 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2714 delete priv
.m_pImpl
;
2715 priv
.m_pImpl
= nullptr;
2717 G_OBJECT_CLASS (lok_doc_view_parent_class
)->finalize (object
);
2720 // kicks the mainloop awake
2721 static gboolean
timeout_wakeup(void *)
2726 // integrate our mainloop with LOK's
2727 static int lok_poll_callback(void*, int timeoutUs
)
2731 guint timeout
= g_timeout_add(timeoutUs
/ 1000, timeout_wakeup
, nullptr);
2732 g_main_context_iteration(nullptr, true);
2733 g_source_remove(timeout
);
2736 g_main_context_iteration(nullptr, FALSE
);
2741 // thread-safe wakeup of our mainloop
2742 static void lok_wake_callback(void *)
2744 g_main_context_wakeup(nullptr);
2747 static gboolean
spin_lok_loop(void *pData
)
2749 LOKDocView
*pDocView
= LOK_DOC_VIEW (pData
);
2750 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2751 priv
->m_pOffice
->pClass
->runLoop(priv
->m_pOffice
, lok_poll_callback
, lok_wake_callback
, nullptr);
2755 // Update the client's view size
2756 static void updateClientZoom(LOKDocView
*pDocView
)
2758 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2759 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
2760 gint nTileSizePixelsScaled
= nTileSizePixels
* nScaleFactor
;
2761 GError
* error
= nullptr;
2763 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
2764 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_CLIENT_ZOOM
);
2765 pLOEvent
->m_nTilePixelWidth
= nTileSizePixelsScaled
;
2766 pLOEvent
->m_nTilePixelHeight
= nTileSizePixelsScaled
;
2767 pLOEvent
->m_nTileTwipWidth
= pixelToTwip(nTileSizePixelsScaled
, priv
->m_fZoom
* nScaleFactor
);
2768 pLOEvent
->m_nTileTwipHeight
= pixelToTwip(nTileSizePixelsScaled
, priv
->m_fZoom
* nScaleFactor
);
2769 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
2771 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
2772 if (error
!= nullptr)
2774 g_warning("Unable to call LOK_SET_CLIENT_ZOOM: %s", error
->message
);
2775 g_clear_error(&error
);
2777 g_object_unref(task
);
2779 priv
->m_nTileSizeTwips
= pixelToTwip(nTileSizePixelsScaled
, priv
->m_fZoom
* nScaleFactor
);
2782 static gboolean
lok_doc_view_initable_init (GInitable
*initable
, GCancellable
* /*cancellable*/, GError
**error
)
2784 LOKDocView
*pDocView
= LOK_DOC_VIEW (initable
);
2785 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2787 if (priv
->m_pOffice
!= nullptr)
2790 if (priv
->m_bUnipoll
)
2791 (void)g_setenv("SAL_LOK_OPTIONS", "unipoll", FALSE
);
2793 static const char testingLangs
[] = "de_DE en_GB en_US es_ES fr_FR it nl pt_BR pt_PT ru";
2794 (void)g_setenv("LOK_ALLOWLIST_LANGUAGES", testingLangs
, FALSE
);
2796 priv
->m_pOffice
= lok_init_2(priv
->m_aLOPath
.c_str(), priv
->m_aUserProfileURL
.empty() ? nullptr : priv
->m_aUserProfileURL
.c_str());
2798 if (priv
->m_pOffice
== nullptr)
2801 g_quark_from_static_string ("LOK initialization error"), 0,
2802 "Failed to get LibreOfficeKit context. Make sure path (%s) is correct",
2803 priv
->m_aLOPath
.c_str());
2806 priv
->m_nLOKFeatures
|= LOK_FEATURE_PART_IN_INVALIDATION_CALLBACK
;
2807 priv
->m_nLOKFeatures
|= LOK_FEATURE_VIEWID_IN_VISCURSOR_INVALIDATION_CALLBACK
;
2808 priv
->m_pOffice
->pClass
->setOptionalFeatures(priv
->m_pOffice
, priv
->m_nLOKFeatures
);
2810 if (priv
->m_bUnipoll
)
2811 g_idle_add(spin_lok_loop
, pDocView
);
2816 static void lok_doc_view_initable_iface_init (GInitableIface
*iface
)
2818 iface
->init
= lok_doc_view_initable_init
;
2821 static void lok_doc_view_class_init (LOKDocViewClass
* pClass
)
2823 GObjectClass
*pGObjectClass
= G_OBJECT_CLASS(pClass
);
2824 GtkWidgetClass
*pWidgetClass
= GTK_WIDGET_CLASS(pClass
);
2826 pGObjectClass
->get_property
= lok_doc_view_get_property
;
2827 pGObjectClass
->set_property
= lok_doc_view_set_property
;
2828 pGObjectClass
->finalize
= lok_doc_view_finalize
;
2830 pWidgetClass
->draw
= lok_doc_view_draw
;
2831 pWidgetClass
->button_press_event
= lok_doc_view_signal_button
;
2832 pWidgetClass
->button_release_event
= lok_doc_view_signal_button
;
2833 pWidgetClass
->key_press_event
= signalKey
;
2834 pWidgetClass
->key_release_event
= signalKey
;
2835 pWidgetClass
->motion_notify_event
= lok_doc_view_signal_motion
;
2836 pWidgetClass
->destroy
= lok_doc_view_destroy
;
2839 * LOKDocView:lopath:
2841 * The absolute path of the LibreOffice install.
2843 properties
[PROP_LO_PATH
] =
2844 g_param_spec_string("lopath",
2846 "LibreOffice Install Path",
2848 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2849 G_PARAM_CONSTRUCT_ONLY
|
2850 G_PARAM_STATIC_STRINGS
));
2853 * LOKDocView:unipoll:
2855 * Whether we use our own unified polling mainloop in place of glib's
2857 properties
[PROP_LO_UNIPOLL
] =
2858 g_param_spec_boolean("unipoll",
2860 "Whether we use a custom unified polling loop",
2862 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2863 G_PARAM_CONSTRUCT_ONLY
|
2864 G_PARAM_STATIC_STRINGS
));
2866 * LOKDocView:lopointer:
2868 * A LibreOfficeKit* in case lok_init() is already called
2871 properties
[PROP_LO_POINTER
] =
2872 g_param_spec_pointer("lopointer",
2874 "A LibreOfficeKit* from lok_init()",
2875 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2876 G_PARAM_CONSTRUCT_ONLY
|
2877 G_PARAM_STATIC_STRINGS
));
2880 * LOKDocView:userprofileurl:
2882 * The absolute path of the LibreOffice user profile.
2884 properties
[PROP_USER_PROFILE_URL
] =
2885 g_param_spec_string("userprofileurl",
2886 "User profile path",
2887 "LibreOffice user profile path",
2889 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2890 G_PARAM_CONSTRUCT_ONLY
|
2891 G_PARAM_STATIC_STRINGS
));
2894 * LOKDocView:docpath:
2896 * The path of the document that is currently being viewed.
2898 properties
[PROP_DOC_PATH
] =
2899 g_param_spec_string("docpath",
2901 "The URI of the document to open",
2903 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2904 G_PARAM_STATIC_STRINGS
));
2907 * LOKDocView:docpointer:
2909 * A LibreOfficeKitDocument* in case documentLoad() is already called
2912 properties
[PROP_DOC_POINTER
] =
2913 g_param_spec_pointer("docpointer",
2915 "A LibreOfficeKitDocument* from documentLoad()",
2916 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2917 G_PARAM_STATIC_STRINGS
));
2920 * LOKDocView:editable:
2922 * Whether the document loaded inside of #LOKDocView is editable or not.
2924 properties
[PROP_EDITABLE
] =
2925 g_param_spec_boolean("editable",
2927 "Whether the content is in edit mode or not",
2929 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2930 G_PARAM_STATIC_STRINGS
));
2933 * LOKDocView:load-progress:
2935 * The percent completion of the current loading operation of the
2936 * document. This can be used for progress bars. Note that this is not a
2937 * very accurate progress indicator, and its value might reset it couple of
2938 * times to 0 and start again. You should not rely on its numbers.
2940 properties
[PROP_LOAD_PROGRESS
] =
2941 g_param_spec_double("load-progress",
2942 "Estimated Load Progress",
2943 "Shows the progress of the document load operation",
2945 static_cast<GParamFlags
>(G_PARAM_READABLE
|
2946 G_PARAM_STATIC_STRINGS
));
2949 * LOKDocView:zoom-level:
2951 * The current zoom level of the document loaded inside #LOKDocView. The
2952 * default value is 1.0.
2954 properties
[PROP_ZOOM
] =
2955 g_param_spec_float("zoom-level",
2957 "The current zoom level of the content",
2959 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2960 G_PARAM_STATIC_STRINGS
));
2963 * LOKDocView:is-loading:
2965 * Whether the requested document is being loaded or not. %TRUE if it is
2966 * being loaded, otherwise %FALSE.
2968 properties
[PROP_IS_LOADING
] =
2969 g_param_spec_boolean("is-loading",
2971 "Whether the view is loading a document",
2973 static_cast<GParamFlags
>(G_PARAM_READABLE
|
2974 G_PARAM_STATIC_STRINGS
));
2977 * LOKDocView:is-initialized:
2979 * Whether the requested document has completely loaded or not.
2981 properties
[PROP_IS_INITIALIZED
] =
2982 g_param_spec_boolean("is-initialized",
2984 "Whether the view has completely initialized",
2986 static_cast<GParamFlags
>(G_PARAM_READABLE
|
2987 G_PARAM_STATIC_STRINGS
));
2990 * LOKDocView:doc-width:
2992 * The width of the currently loaded document in #LOKDocView in twips.
2994 properties
[PROP_DOC_WIDTH
] =
2995 g_param_spec_long("doc-width",
2997 "Width of the document in twips",
2999 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
3000 G_PARAM_STATIC_STRINGS
));
3003 * LOKDocView:doc-height:
3005 * The height of the currently loaded document in #LOKDocView in twips.
3007 properties
[PROP_DOC_HEIGHT
] =
3008 g_param_spec_long("doc-height",
3010 "Height of the document in twips",
3012 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
3013 G_PARAM_STATIC_STRINGS
));
3016 * LOKDocView:can-zoom-in:
3018 * It tells whether the view can further be zoomed in or not.
3020 properties
[PROP_CAN_ZOOM_IN
] =
3021 g_param_spec_boolean("can-zoom-in",
3023 "Whether the view can be zoomed in further",
3025 static_cast<GParamFlags
>(G_PARAM_READABLE
3026 | G_PARAM_STATIC_STRINGS
));
3029 * LOKDocView:can-zoom-out:
3031 * It tells whether the view can further be zoomed out or not.
3033 properties
[PROP_CAN_ZOOM_OUT
] =
3034 g_param_spec_boolean("can-zoom-out",
3036 "Whether the view can be zoomed out further",
3038 static_cast<GParamFlags
>(G_PARAM_READABLE
3039 | G_PARAM_STATIC_STRINGS
));
3042 * LOKDocView:doc-password:
3044 * Set it to true if client supports providing password for viewing
3045 * password protected documents
3047 properties
[PROP_DOC_PASSWORD
] =
3048 g_param_spec_boolean("doc-password",
3049 "Document password capability",
3050 "Whether client supports providing document passwords",
3052 static_cast<GParamFlags
>(G_PARAM_READWRITE
3053 | G_PARAM_STATIC_STRINGS
));
3056 * LOKDocView:doc-password-to-modify:
3058 * Set it to true if client supports providing password for edit-protected documents
3060 properties
[PROP_DOC_PASSWORD_TO_MODIFY
] =
3061 g_param_spec_boolean("doc-password-to-modify",
3062 "Edit document password capability",
3063 "Whether the client supports providing passwords to edit documents",
3065 static_cast<GParamFlags
>(G_PARAM_READWRITE
3066 | G_PARAM_STATIC_STRINGS
));
3069 * LOKDocView:tiled-annotations-rendering:
3071 * Set it to false if client does not want LO to render comments in tiles and
3072 * instead interested in using comments API to access comments
3074 properties
[PROP_TILED_ANNOTATIONS
] =
3075 g_param_spec_boolean("tiled-annotations",
3076 "Render comments in tiles",
3077 "Whether the client wants in tile comment rendering",
3079 static_cast<GParamFlags
>(G_PARAM_READWRITE
3080 | G_PARAM_STATIC_STRINGS
));
3082 g_object_class_install_properties(pGObjectClass
, PROP_LAST
, properties
);
3085 * LOKDocView::load-changed:
3086 * @pDocView: the #LOKDocView on which the signal is emitted
3087 * @fLoadProgress: the new progress value
3089 doc_view_signals
[LOAD_CHANGED
] =
3090 g_signal_new("load-changed",
3091 G_TYPE_FROM_CLASS (pGObjectClass
),
3095 g_cclosure_marshal_VOID__DOUBLE
,
3100 * LOKDocView::edit-changed:
3101 * @pDocView: the #LOKDocView on which the signal is emitted
3102 * @bEdit: the new edit value of the view
3104 doc_view_signals
[EDIT_CHANGED
] =
3105 g_signal_new("edit-changed",
3106 G_TYPE_FROM_CLASS (pGObjectClass
),
3110 g_cclosure_marshal_VOID__BOOLEAN
,
3115 * LOKDocView::command-changed:
3116 * @pDocView: the #LOKDocView on which the signal is emitted
3117 * @aCommand: the command that was changed
3119 doc_view_signals
[COMMAND_CHANGED
] =
3120 g_signal_new("command-changed",
3121 G_TYPE_FROM_CLASS(pGObjectClass
),
3125 g_cclosure_marshal_VOID__STRING
,
3130 * LOKDocView::search-not-found:
3131 * @pDocView: the #LOKDocView on which the signal is emitted
3132 * @aCommand: the string for which the search was not found.
3134 doc_view_signals
[SEARCH_NOT_FOUND
] =
3135 g_signal_new("search-not-found",
3136 G_TYPE_FROM_CLASS(pGObjectClass
),
3140 g_cclosure_marshal_VOID__STRING
,
3145 * LOKDocView::part-changed:
3146 * @pDocView: the #LOKDocView on which the signal is emitted
3147 * @aCommand: the part number which the view changed to
3149 doc_view_signals
[PART_CHANGED
] =
3150 g_signal_new("part-changed",
3151 G_TYPE_FROM_CLASS(pGObjectClass
),
3155 g_cclosure_marshal_VOID__INT
,
3160 * LOKDocView::size-changed:
3161 * @pDocView: the #LOKDocView on which the signal is emitted
3162 * @aCommand: NULL, we just notify that want to notify the UI elements that are interested.
3164 doc_view_signals
[SIZE_CHANGED
] =
3165 g_signal_new("size-changed",
3166 G_TYPE_FROM_CLASS(pGObjectClass
),
3170 g_cclosure_marshal_VOID__VOID
,
3175 * LOKDocView::hyperlinked-clicked:
3176 * @pDocView: the #LOKDocView on which the signal is emitted
3177 * @aHyperlink: the URI which the application should handle
3179 doc_view_signals
[HYPERLINK_CLICKED
] =
3180 g_signal_new("hyperlink-clicked",
3181 G_TYPE_FROM_CLASS(pGObjectClass
),
3185 g_cclosure_marshal_VOID__STRING
,
3190 * LOKDocView::cursor-changed:
3191 * @pDocView: the #LOKDocView on which the signal is emitted
3192 * @nX: The new cursor position (X coordinate) in pixels
3193 * @nY: The new cursor position (Y coordinate) in pixels
3194 * @nWidth: The width of new cursor
3195 * @nHeight: The height of new cursor
3197 doc_view_signals
[CURSOR_CHANGED
] =
3198 g_signal_new("cursor-changed",
3199 G_TYPE_FROM_CLASS(pGObjectClass
),
3203 g_cclosure_marshal_generic
,
3205 G_TYPE_INT
, G_TYPE_INT
,
3206 G_TYPE_INT
, G_TYPE_INT
);
3209 * LOKDocView::search-result-count:
3210 * @pDocView: the #LOKDocView on which the signal is emitted
3211 * @aCommand: number of matches.
3213 doc_view_signals
[SEARCH_RESULT_COUNT
] =
3214 g_signal_new("search-result-count",
3215 G_TYPE_FROM_CLASS(pGObjectClass
),
3219 g_cclosure_marshal_VOID__STRING
,
3224 * LOKDocView::command-result:
3225 * @pDocView: the #LOKDocView on which the signal is emitted
3226 * @aCommand: JSON containing the info about the command that finished,
3227 * and its success status.
3229 doc_view_signals
[COMMAND_RESULT
] =
3230 g_signal_new("command-result",
3231 G_TYPE_FROM_CLASS(pGObjectClass
),
3235 g_cclosure_marshal_VOID__STRING
,
3240 * LOKDocView::address-changed:
3241 * @pDocView: the #LOKDocView on which the signal is emitted
3242 * @aCommand: formula text content
3244 doc_view_signals
[ADDRESS_CHANGED
] =
3245 g_signal_new("address-changed",
3246 G_TYPE_FROM_CLASS(pGObjectClass
),
3250 g_cclosure_marshal_VOID__STRING
,
3255 * LOKDocView::formula-changed:
3256 * @pDocView: the #LOKDocView on which the signal is emitted
3257 * @aCommand: formula text content
3259 doc_view_signals
[FORMULA_CHANGED
] =
3260 g_signal_new("formula-changed",
3261 G_TYPE_FROM_CLASS(pGObjectClass
),
3265 g_cclosure_marshal_VOID__STRING
,
3270 * LOKDocView::text-selection:
3271 * @pDocView: the #LOKDocView on which the signal is emitted
3272 * @bIsTextSelected: whether text selected is non-null
3274 doc_view_signals
[TEXT_SELECTION
] =
3275 g_signal_new("text-selection",
3276 G_TYPE_FROM_CLASS(pGObjectClass
),
3280 g_cclosure_marshal_VOID__BOOLEAN
,
3285 * LOKDocView::password-required:
3286 * @pDocView: the #LOKDocView on which the signal is emitted
3287 * @pUrl: URL of the document for which password is required
3288 * @bModify: whether password id required to modify the document
3289 * This is true when password is required to edit the document,
3290 * while it can still be viewed without password. In such cases, provide a NULL
3291 * password for read-only access to the document.
3292 * If false, password is required for opening the document, and document
3293 * cannot be opened without providing a valid password.
3295 * Password must be provided by calling lok_doc_view_set_document_password
3296 * function with pUrl as provided by the callback.
3298 * Upon entering an invalid password, another `password-required` signal is
3300 * Upon entering a valid password, document starts to load.
3301 * Upon entering a NULL password: if bModify is %TRUE, document starts to
3302 * open in view-only mode, else loading of document is aborted.
3304 doc_view_signals
[PASSWORD_REQUIRED
] =
3305 g_signal_new("password-required",
3306 G_TYPE_FROM_CLASS(pGObjectClass
),
3310 g_cclosure_marshal_generic
,
3316 * LOKDocView::comment:
3317 * @pDocView: the #LOKDocView on which the signal is emitted
3318 * @pComment: the JSON string containing comment notification
3319 * The has following structure containing the information telling whether
3320 * the comment has been added, deleted or modified.
3327 * "author": "Unknown Author",
3328 * "text": "This is a comment",
3329 * "dateTime": "2016-08-18T13:13:00",
3330 * "anchorPos": "4529, 3906",
3331 * "textRange": "1418, 3906, 3111, 919"
3334 * 'action' can be 'Add', 'Remove' or 'Modify' depending on whether
3335 * comment has been added, removed or modified.
3336 * 'parent' is a non-zero comment id if this comment is a reply comment,
3337 * otherwise it's a root comment.
3339 doc_view_signals
[COMMENT
] =
3340 g_signal_new("comment",
3341 G_TYPE_FROM_CLASS(pGObjectClass
),
3345 g_cclosure_marshal_generic
,
3350 * LOKDocView::ruler:
3351 * @pDocView: the #LOKDocView on which the signal is emitted
3352 * @pPayload: the JSON string containing the information about ruler properties
3354 * The payload format is:
3359 * "leftOffset": "...",
3360 * "pageOffset": "...",
3361 * "pageWidth": "...",
3365 doc_view_signals
[RULER
] =
3366 g_signal_new("ruler",
3367 G_TYPE_FROM_CLASS(pGObjectClass
),
3371 g_cclosure_marshal_generic
,
3376 * LOKDocView::window::
3377 * @pDocView: the #LOKDocView on which the signal is emitted
3378 * @pPayload: the JSON string containing the information about the window
3380 * This signal emits information about external windows like dialogs, autopopups for now.
3382 * The payload format of pPayload is:
3385 * "id": "unique integer id of the dialog",
3386 * "action": "<see below>",
3387 * "type": "<see below>"
3388 * "rectangle": "x, y, width, height"
3391 * "type" tells the type of the window the action is associated with
3392 * - "dialog" - window is a dialog
3393 * - "child" - window is a floating window (combo boxes, etc.)
3395 * "action" can take following values:
3396 * - "created" - window is created in the backend, client can render it now
3397 * - "title_changed" - window's title is changed
3398 * - "size_changed" - window's size is changed
3399 * - "invalidate" - the area as described by "rectangle" is invalidated
3400 * Clients must request the new area
3401 * - "cursor_invalidate" - cursor is invalidated. New position is in "rectangle"
3402 * - "cursor_visible" - cursor visible status is changed. Status is available
3403 * in "visible" field
3404 * - "close" - window is closed
3406 doc_view_signals
[WINDOW
] =
3407 g_signal_new("window",
3408 G_TYPE_FROM_CLASS(pGObjectClass
),
3412 g_cclosure_marshal_generic
,
3417 * LOKDocView::invalidate-header::
3418 * @pDocView: the #LOKDocView on which the signal is emitted
3419 * @pPayload: can be either "row", "column", or "all".
3421 * The column/row header is no more valid because of a column/row insertion
3422 * or a similar event. Clients must query a new column/row header set.
3424 * The payload says if we are invalidating a row or column header
3426 doc_view_signals
[INVALIDATE_HEADER
] =
3427 g_signal_new("invalidate-header",
3428 G_TYPE_FROM_CLASS(pGObjectClass
),
3432 g_cclosure_marshal_generic
,
3437 SAL_DLLPUBLIC_EXPORT GtkWidget
*
3438 lok_doc_view_new (const gchar
* pPath
, GCancellable
*cancellable
, GError
**error
)
3440 return GTK_WIDGET (g_initable_new (LOK_TYPE_DOC_VIEW
, cancellable
, error
,
3441 "lopath", pPath
== nullptr ? LOK_PATH
: pPath
,
3442 "halign", GTK_ALIGN_CENTER
,
3443 "valign", GTK_ALIGN_CENTER
,
3447 SAL_DLLPUBLIC_EXPORT GtkWidget
*
3448 lok_doc_view_new_from_user_profile (const gchar
* pPath
, const gchar
* pUserProfile
, GCancellable
*cancellable
, GError
**error
)
3450 return GTK_WIDGET(g_initable_new(LOK_TYPE_DOC_VIEW
, cancellable
, error
,
3451 "lopath", pPath
== nullptr ? LOK_PATH
: pPath
,
3452 "userprofileurl", pUserProfile
,
3453 "halign", GTK_ALIGN_CENTER
,
3454 "valign", GTK_ALIGN_CENTER
,
3458 SAL_DLLPUBLIC_EXPORT GtkWidget
* lok_doc_view_new_from_widget(LOKDocView
* pOldLOKDocView
,
3459 const gchar
* pRenderingArguments
)
3461 LOKDocViewPrivate
& pOldPriv
= getPrivate(pOldLOKDocView
);
3462 GtkWidget
* pNewDocView
= GTK_WIDGET(g_initable_new(LOK_TYPE_DOC_VIEW
, /*cancellable=*/nullptr, /*error=*/nullptr,
3463 "lopath", pOldPriv
->m_aLOPath
.c_str(),
3464 "userprofileurl", pOldPriv
->m_aUserProfileURL
.c_str(),
3465 "lopointer", pOldPriv
->m_pOffice
,
3466 "docpointer", pOldPriv
->m_pDocument
,
3467 "halign", GTK_ALIGN_CENTER
,
3468 "valign", GTK_ALIGN_CENTER
,
3471 // No documentLoad(), just a createView().
3472 LibreOfficeKitDocument
* pDocument
= lok_doc_view_get_document(LOK_DOC_VIEW(pNewDocView
));
3473 LOKDocViewPrivate
& pNewPriv
= getPrivate(LOK_DOC_VIEW(pNewDocView
));
3474 // Store the view id only later in postDocumentLoad(), as
3475 // initializeForRendering() changes the id in Impress.
3476 pDocument
->pClass
->createView(pDocument
);
3477 pNewPriv
->m_aRenderingArguments
= pRenderingArguments
;
3479 postDocumentLoad(pNewDocView
);
3483 SAL_DLLPUBLIC_EXPORT gboolean
3484 lok_doc_view_open_document_finish (LOKDocView
* pDocView
, GAsyncResult
* res
, GError
** error
)
3486 GTask
* task
= G_TASK(res
);
3488 g_return_val_if_fail(g_task_is_valid(res
, pDocView
), false);
3489 g_return_val_if_fail(g_task_get_source_tag(task
) == lok_doc_view_open_document
, false);
3490 g_return_val_if_fail(error
== nullptr || *error
== nullptr, false);
3492 return g_task_propagate_boolean(task
, error
);
3495 SAL_DLLPUBLIC_EXPORT
void
3496 lok_doc_view_open_document (LOKDocView
* pDocView
,
3498 const gchar
* pRenderingArguments
,
3499 GCancellable
* cancellable
,
3500 GAsyncReadyCallback callback
,
3503 GTask
* task
= g_task_new(pDocView
, cancellable
, callback
, userdata
);
3504 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3505 GError
* error
= nullptr;
3507 LOEvent
* pLOEvent
= new LOEvent(LOK_LOAD_DOC
);
3509 g_object_set(G_OBJECT(pDocView
), "docpath", pPath
, nullptr);
3510 if (pRenderingArguments
)
3511 priv
->m_aRenderingArguments
= pRenderingArguments
;
3512 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
3513 g_task_set_source_tag(task
, reinterpret_cast<gpointer
>(lok_doc_view_open_document
));
3515 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
3516 if (error
!= nullptr)
3518 g_warning("Unable to call LOK_LOAD_DOC: %s", error
->message
);
3519 g_clear_error(&error
);
3521 g_object_unref(task
);
3524 SAL_DLLPUBLIC_EXPORT LibreOfficeKitDocument
*
3525 lok_doc_view_get_document (LOKDocView
* pDocView
)
3527 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3528 return priv
->m_pDocument
;
3531 SAL_DLLPUBLIC_EXPORT
void
3532 lok_doc_view_set_visible_area (LOKDocView
* pDocView
, GdkRectangle
* pVisibleArea
)
3537 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3538 priv
->m_aVisibleArea
= *pVisibleArea
;
3539 priv
->m_bVisibleAreaSet
= true;
3543 // This used to be rtl::math::approxEqual() but since that isn't inline anymore
3544 // in rtl/math.hxx and was moved into libuno_sal as rtl_math_approxEqual() to
3545 // cater for representable integer cases and we don't want to link against
3546 // libuno_sal, we'll have to have an own implementation. The special large
3547 // integer cases seems not be needed here.
3548 bool lok_approxEqual(double a
, double b
)
3550 static const double e48
= 1.0 / (16777216.0 * 16777216.0);
3553 if (a
== 0.0 || b
== 0.0)
3555 const double d
= fabs(a
- b
);
3556 return (d
< fabs(a
) * e48
&& d
< fabs(b
) * e48
);
3560 SAL_DLLPUBLIC_EXPORT
void
3561 lok_doc_view_set_zoom (LOKDocView
* pDocView
, float fZoom
)
3563 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3565 if (!priv
->m_pDocument
)
3568 // Clamp the input value in [MIN_ZOOM, MAX_ZOOM]
3569 fZoom
= fZoom
< MIN_ZOOM
? MIN_ZOOM
: fZoom
;
3570 fZoom
= std::min(fZoom
, MAX_ZOOM
);
3572 if (lok_approxEqual(fZoom
, priv
->m_fZoom
))
3575 gint nScaleFactor
= gtk_widget_get_scale_factor(GTK_WIDGET(pDocView
));
3576 gint nTileSizePixelsScaled
= nTileSizePixels
* nScaleFactor
;
3577 priv
->m_fZoom
= fZoom
;
3578 long nDocumentWidthPixels
= twipToPixel(priv
->m_nDocumentWidthTwips
, fZoom
* nScaleFactor
);
3579 long nDocumentHeightPixels
= twipToPixel(priv
->m_nDocumentHeightTwips
, fZoom
* nScaleFactor
);
3580 // Total number of columns in this document.
3581 guint nColumns
= ceil(static_cast<double>(nDocumentWidthPixels
) / nTileSizePixelsScaled
);
3582 priv
->m_pTileBuffer
= std::make_unique
<TileBuffer
>(nColumns
, nScaleFactor
);
3583 gtk_widget_set_size_request(GTK_WIDGET(pDocView
),
3584 nDocumentWidthPixels
/ nScaleFactor
,
3585 nDocumentHeightPixels
/ nScaleFactor
);
3587 g_object_notify_by_pspec(G_OBJECT(pDocView
), properties
[PROP_ZOOM
]);
3589 // set properties to indicate if view can be further zoomed in/out
3590 bool bCanZoomIn
= priv
->m_fZoom
< MAX_ZOOM
;
3591 bool bCanZoomOut
= priv
->m_fZoom
> MIN_ZOOM
;
3592 if (bCanZoomIn
!= bool(priv
->m_bCanZoomIn
))
3594 priv
->m_bCanZoomIn
= bCanZoomIn
;
3595 g_object_notify_by_pspec(G_OBJECT(pDocView
), properties
[PROP_CAN_ZOOM_IN
]);
3597 if (bCanZoomOut
!= bool(priv
->m_bCanZoomOut
))
3599 priv
->m_bCanZoomOut
= bCanZoomOut
;
3600 g_object_notify_by_pspec(G_OBJECT(pDocView
), properties
[PROP_CAN_ZOOM_OUT
]);
3603 updateClientZoom(pDocView
);
3606 SAL_DLLPUBLIC_EXPORT gfloat
3607 lok_doc_view_get_zoom (LOKDocView
* pDocView
)
3609 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3610 return priv
->m_fZoom
;
3613 SAL_DLLPUBLIC_EXPORT gint
3614 lok_doc_view_get_parts (LOKDocView
* pDocView
)
3616 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3617 if (!priv
->m_pDocument
)
3620 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
3621 std::stringstream ss
;
3622 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
3623 g_info("%s", ss
.str().c_str());
3624 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
3625 return priv
->m_pDocument
->pClass
->getParts( priv
->m_pDocument
);
3628 SAL_DLLPUBLIC_EXPORT gint
3629 lok_doc_view_get_part (LOKDocView
* pDocView
)
3631 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3632 if (!priv
->m_pDocument
)
3635 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
3636 std::stringstream ss
;
3637 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
3638 g_info("%s", ss
.str().c_str());
3639 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
3640 return priv
->m_pDocument
->pClass
->getPart( priv
->m_pDocument
);
3643 SAL_DLLPUBLIC_EXPORT
void
3644 lok_doc_view_set_part (LOKDocView
* pDocView
, int nPart
)
3646 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3647 if (!priv
->m_pDocument
)
3650 if (nPart
< 0 || nPart
>= priv
->m_nParts
)
3652 g_warning("Invalid part request : %d", nPart
);
3656 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
3657 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_PART
);
3658 GError
* error
= nullptr;
3660 pLOEvent
->m_nPart
= nPart
;
3661 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
3663 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
3664 if (error
!= nullptr)
3666 g_warning("Unable to call LOK_SET_PART: %s", error
->message
);
3667 g_clear_error(&error
);
3669 g_object_unref(task
);
3670 priv
->m_nPartId
= nPart
;
3673 SAL_DLLPUBLIC_EXPORT gchar
*
3674 lok_doc_view_get_part_name (LOKDocView
* pDocView
, int nPart
)
3676 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3677 if (!priv
->m_pDocument
)
3680 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
3681 std::stringstream ss
;
3682 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
3683 g_info("%s", ss
.str().c_str());
3684 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
3685 return priv
->m_pDocument
->pClass
->getPartName( priv
->m_pDocument
, nPart
);
3688 SAL_DLLPUBLIC_EXPORT
void
3689 lok_doc_view_set_partmode(LOKDocView
* pDocView
,
3692 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3693 if (!priv
->m_pDocument
)
3696 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
3697 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_PARTMODE
);
3698 GError
* error
= nullptr;
3700 pLOEvent
->m_nPartMode
= nPartMode
;
3701 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
3703 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
3704 if (error
!= nullptr)
3706 g_warning("Unable to call LOK_SET_PARTMODE: %s", error
->message
);
3707 g_clear_error(&error
);
3709 g_object_unref(task
);
3712 SAL_DLLPUBLIC_EXPORT
void
3713 lok_doc_view_reset_view(LOKDocView
* pDocView
)
3715 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3717 if (priv
->m_pTileBuffer
!= nullptr)
3718 priv
->m_pTileBuffer
->resetAllTiles();
3719 priv
->m_nLoadProgress
= 0.0;
3721 memset(&priv
->m_aVisibleCursor
, 0, sizeof(priv
->m_aVisibleCursor
));
3722 priv
->m_bCursorOverlayVisible
= false;
3723 priv
->m_bCursorVisible
= false;
3725 priv
->m_nLastButtonPressTime
= 0;
3726 priv
->m_nLastButtonReleaseTime
= 0;
3727 priv
->m_aTextSelectionRectangles
.clear();
3729 memset(&priv
->m_aTextSelectionStart
, 0, sizeof(priv
->m_aTextSelectionStart
));
3730 memset(&priv
->m_aTextSelectionEnd
, 0, sizeof(priv
->m_aTextSelectionEnd
));
3731 memset(&priv
->m_aGraphicSelection
, 0, sizeof(priv
->m_aGraphicSelection
));
3732 priv
->m_bInDragGraphicSelection
= false;
3733 memset(&priv
->m_aCellCursor
, 0, sizeof(priv
->m_aCellCursor
));
3735 cairo_surface_destroy(priv
->m_pHandleStart
);
3736 priv
->m_pHandleStart
= nullptr;
3737 memset(&priv
->m_aHandleStartRect
, 0, sizeof(priv
->m_aHandleStartRect
));
3738 priv
->m_bInDragStartHandle
= false;
3740 cairo_surface_destroy(priv
->m_pHandleMiddle
);
3741 priv
->m_pHandleMiddle
= nullptr;
3742 memset(&priv
->m_aHandleMiddleRect
, 0, sizeof(priv
->m_aHandleMiddleRect
));
3743 priv
->m_bInDragMiddleHandle
= false;
3745 cairo_surface_destroy(priv
->m_pHandleEnd
);
3746 priv
->m_pHandleEnd
= nullptr;
3747 memset(&priv
->m_aHandleEndRect
, 0, sizeof(priv
->m_aHandleEndRect
));
3748 priv
->m_bInDragEndHandle
= false;
3750 memset(&priv
->m_aGraphicHandleRects
, 0, sizeof(priv
->m_aGraphicHandleRects
));
3751 memset(&priv
->m_bInDragGraphicHandles
, 0, sizeof(priv
->m_bInDragGraphicHandles
));
3753 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
3756 SAL_DLLPUBLIC_EXPORT
void
3757 lok_doc_view_set_edit(LOKDocView
* pDocView
,
3760 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3761 if (!priv
->m_pDocument
)
3764 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
3765 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_EDIT
);
3766 GError
* error
= nullptr;
3768 pLOEvent
->m_bEdit
= bEdit
;
3769 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
3771 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
3772 if (error
!= nullptr)
3774 g_warning("Unable to call LOK_SET_EDIT: %s", error
->message
);
3775 g_clear_error(&error
);
3777 g_object_unref(task
);
3780 SAL_DLLPUBLIC_EXPORT gboolean
3781 lok_doc_view_get_edit (LOKDocView
* pDocView
)
3783 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3784 return priv
->m_bEdit
;
3787 SAL_DLLPUBLIC_EXPORT
void
3788 lok_doc_view_post_command (LOKDocView
* pDocView
,
3789 const gchar
* pCommand
,
3790 const gchar
* pArguments
,
3791 gboolean bNotifyWhenFinished
)
3793 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3794 if (!priv
->m_pDocument
)
3798 LOKPostCommand(pDocView
, pCommand
, pArguments
, bNotifyWhenFinished
);
3800 g_info ("LOK_POST_COMMAND: ignoring commands in view-only mode");
3803 SAL_DLLPUBLIC_EXPORT gchar
*
3804 lok_doc_view_get_command_values (LOKDocView
* pDocView
,
3805 const gchar
* pCommand
)
3807 g_return_val_if_fail (LOK_IS_DOC_VIEW (pDocView
), nullptr);
3808 g_return_val_if_fail (pCommand
!= nullptr, nullptr);
3810 LibreOfficeKitDocument
* pDocument
= lok_doc_view_get_document(pDocView
);
3814 return pDocument
->pClass
->getCommandValues(pDocument
, pCommand
);
3817 SAL_DLLPUBLIC_EXPORT
void
3818 lok_doc_view_find_prev (LOKDocView
* pDocView
,
3820 gboolean bHighlightAll
)
3822 doSearch(pDocView
, pText
, true, bHighlightAll
);
3825 SAL_DLLPUBLIC_EXPORT
void
3826 lok_doc_view_find_next (LOKDocView
* pDocView
,
3828 gboolean bHighlightAll
)
3830 doSearch(pDocView
, pText
, false, bHighlightAll
);
3833 SAL_DLLPUBLIC_EXPORT
void
3834 lok_doc_view_highlight_all (LOKDocView
* pDocView
,
3837 doSearch(pDocView
, pText
, false, true);
3840 SAL_DLLPUBLIC_EXPORT gchar
*
3841 lok_doc_view_copy_selection (LOKDocView
* pDocView
,
3842 const gchar
* pMimeType
,
3843 gchar
** pUsedMimeType
)
3845 LibreOfficeKitDocument
* pDocument
= lok_doc_view_get_document(pDocView
);
3849 std::stringstream ss
;
3850 ss
<< "lok::Document::getTextSelection('" << pMimeType
<< "')";
3851 g_info("%s", ss
.str().c_str());
3852 return pDocument
->pClass
->getTextSelection(pDocument
, pMimeType
, pUsedMimeType
);
3855 SAL_DLLPUBLIC_EXPORT gboolean
3856 lok_doc_view_paste (LOKDocView
* pDocView
,
3857 const gchar
* pMimeType
,
3861 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3862 LibreOfficeKitDocument
* pDocument
= priv
->m_pDocument
;
3870 g_info ("ignoring paste in view-only mode");
3876 std::stringstream ss
;
3877 ss
<< "lok::Document::paste('" << pMimeType
<< "', '" << std::string(pData
, nSize
) << ", "<<nSize
<<"')";
3878 g_info("%s", ss
.str().c_str());
3879 ret
= pDocument
->pClass
->paste(pDocument
, pMimeType
, pData
, nSize
);
3885 SAL_DLLPUBLIC_EXPORT
void
3886 lok_doc_view_set_document_password (LOKDocView
* pDocView
,
3888 const gchar
* pPassword
)
3890 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3892 priv
->m_pOffice
->pClass
->setDocumentPassword(priv
->m_pOffice
, pURL
, pPassword
);
3895 SAL_DLLPUBLIC_EXPORT gchar
*
3896 lok_doc_view_get_version_info (LOKDocView
* pDocView
)
3898 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3900 return priv
->m_pOffice
->pClass
->getVersionInfo(priv
->m_pOffice
);
3904 SAL_DLLPUBLIC_EXPORT gfloat
3905 lok_doc_view_pixel_to_twip (LOKDocView
* pDocView
, float fInput
)
3907 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3908 return pixelToTwip(fInput
, priv
->m_fZoom
);
3911 SAL_DLLPUBLIC_EXPORT gfloat
3912 lok_doc_view_twip_to_pixel (LOKDocView
* pDocView
, float fInput
)
3914 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3915 return twipToPixel(fInput
, priv
->m_fZoom
);
3918 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */