1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <sal/types.h>
19 #include <boost/property_tree/json_parser.hpp>
21 #include <com/sun/star/awt/Key.hpp>
22 #include <LibreOfficeKit/LibreOfficeKit.h>
23 #include <LibreOfficeKit/LibreOfficeKitInit.h>
24 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
25 #include <LibreOfficeKit/LibreOfficeKitGtk.h>
26 #include <vcl/event.hxx>
28 #include "tilebuffer.hxx"
30 #if !GLIB_CHECK_VERSION(2,32,0)
31 #define G_SOURCE_REMOVE FALSE
32 #define G_SOURCE_CONTINUE TRUE
34 #if !GLIB_CHECK_VERSION(2,40,0)
35 #define g_info(...) g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, __VA_ARGS__)
38 // Cursor bitmaps from the installation set.
39 #define CURSOR_HANDLE_DIR "/../share/libreofficekit/"
40 // Number of handles around a graphic selection.
41 #define GRAPHIC_HANDLE_COUNT 8
42 // Maximum Zoom allowed
44 // Minimum Zoom allowed
45 #define MIN_ZOOM 0.25f
47 /// This is expected to be locked during setView(), doSomethingElse() LOK calls.
48 static std::mutex g_aLOKMutex
;
50 /// Same as a GdkRectangle, but also tracks in which part the rectangle is.
54 GdkRectangle m_aRectangle
;
56 ViewRectangle(int nPart
= 0, const GdkRectangle
& rRectangle
= GdkRectangle())
58 m_aRectangle(rRectangle
)
63 /// Same as a list of GdkRectangles, but also tracks in which part the rectangle is.
67 std::vector
<GdkRectangle
> m_aRectangles
;
69 ViewRectangles(int nPart
= 0, const std::vector
<GdkRectangle
>& rRectangles
= std::vector
<GdkRectangle
>())
71 m_aRectangles(rRectangles
)
76 /// Private struct used by this GObject type
77 struct LOKDocViewPrivateImpl
79 std::string m_aLOPath
;
80 std::string m_aUserProfileURL
;
81 std::string m_aDocPath
;
82 std::string m_aRenderingArguments
;
83 gdouble m_nLoadProgress
;
84 gboolean m_bIsLoading
;
85 gboolean m_bInit
; // initializeForRendering() has been called
86 gboolean m_bCanZoomIn
;
87 gboolean m_bCanZoomOut
;
89 LibreOfficeKit
* m_pOffice
;
90 LibreOfficeKitDocument
* m_pDocument
;
92 std::unique_ptr
<TileBuffer
> m_pTileBuffer
;
93 GThreadPool
* lokThreadPool
;
96 glong m_nDocumentWidthTwips
;
97 glong m_nDocumentHeightTwips
;
98 /// View or edit mode.
101 guint64 m_nLOKFeatures
;
102 /// Number of parts in currently loaded document
104 /// Position and size of the visible cursor.
105 GdkRectangle m_aVisibleCursor
;
106 /// Position and size of the view cursors. The current view can only see
107 /// them, can't modify them. Key is the view id.
108 std::map
<int, ViewRectangle
> m_aViewCursors
;
109 /// Cursor overlay is visible or hidden (for blinking).
110 gboolean m_bCursorOverlayVisible
;
111 /// Cursor is visible or hidden (e.g. for graphic selection).
112 gboolean m_bCursorVisible
;
113 /// Visibility of view selections. The current view can only see / them,
114 /// can't modify them. Key is the view id.
115 std::map
<int, bool> m_aViewCursorVisibilities
;
116 /// Time of the last button press.
117 guint32 m_nLastButtonPressTime
;
118 /// Time of the last button release.
119 guint32 m_nLastButtonReleaseTime
;
120 /// Last pressed button (left, right, middle)
121 guint32 m_nLastButtonPressed
;
122 /// Key modifier (ctrl, atl, shift)
123 guint32 m_nKeyModifier
;
124 /// Rectangles of the current text selection.
125 std::vector
<GdkRectangle
> m_aTextSelectionRectangles
;
126 /// Rectangles of view selections. The current view can only see
127 /// them, can't modify them. Key is the view id.
128 std::map
<int, ViewRectangles
> m_aTextViewSelectionRectangles
;
129 /// Position and size of the selection start (as if there would be a cursor caret there).
130 GdkRectangle m_aTextSelectionStart
;
131 /// Position and size of the selection end.
132 GdkRectangle m_aTextSelectionEnd
;
133 GdkRectangle m_aGraphicSelection
;
134 /// Position and size of the graphic view selections. The current view can only
135 /// see them, can't modify them. Key is the view id.
136 std::map
<int, ViewRectangle
> m_aGraphicViewSelections
;
137 GdkRectangle m_aCellCursor
;
138 /// Position and size of the cell view cursors. The current view can only
139 /// see them, can't modify them. Key is the view id.
140 std::map
<int, ViewRectangle
> m_aCellViewCursors
;
141 gboolean m_bInDragGraphicSelection
;
143 /// @name Start/middle/end handle.
145 /// Bitmap of the text selection start handle.
146 cairo_surface_t
* m_pHandleStart
;
147 /// Rectangle of the text selection start handle, to know if the user clicked on it or not
148 GdkRectangle m_aHandleStartRect
;
149 /// If we are in the middle of a drag of the text selection end handle.
150 gboolean m_bInDragStartHandle
;
151 /// Bitmap of the text selection middle handle.
152 cairo_surface_t
* m_pHandleMiddle
;
153 /// Rectangle of the text selection middle handle, to know if the user clicked on it or not
154 GdkRectangle m_aHandleMiddleRect
;
155 /// If we are in the middle of a drag of the text selection middle handle.
156 gboolean m_bInDragMiddleHandle
;
157 /// Bitmap of the text selection end handle.
158 cairo_surface_t
* m_pHandleEnd
;
159 /// Rectangle of the text selection end handle, to know if the user clicked on it or not
160 GdkRectangle m_aHandleEndRect
;
161 /// If we are in the middle of a drag of the text selection end handle.
162 gboolean m_bInDragEndHandle
;
165 /// @name Graphic handles.
167 /// Rectangle of a graphic selection handle, to know if the user clicked on it or not.
168 GdkRectangle m_aGraphicHandleRects
[8];
169 /// If we are in the middle of a drag of a graphic selection handle.
170 gboolean m_bInDragGraphicHandles
[8];
173 /// View ID, returned by createView() or 0 by default.
176 /// Cached part ID, returned by getPart().
179 /// Cached document type, returned by getDocumentType().
180 LibreOfficeKitDocumentType m_eDocumentType
;
182 /// Contains a freshly set zoom level: logic size of a tile.
183 /// It gets reset back to 0 when LOK was informed about this zoom change.
184 int m_nTileSizeTwips
;
186 GdkRectangle m_aVisibleArea
;
187 bool m_bVisibleAreaSet
;
189 /// Event source ID for handleTimeout() of this widget.
192 /// Rectangles of view locks. The current view can only see
193 /// them, can't modify them. Key is the view id.
194 std::map
<int, ViewRectangle
> m_aViewLockRectangles
;
196 LOKDocViewPrivateImpl()
197 : m_nLoadProgress(0),
204 m_pDocument(nullptr),
205 lokThreadPool(nullptr),
207 m_nDocumentWidthTwips(0),
208 m_nDocumentHeightTwips(0),
212 m_aVisibleCursor({0, 0, 0, 0}),
213 m_bCursorOverlayVisible(false),
214 m_bCursorVisible(true),
215 m_nLastButtonPressTime(0),
216 m_nLastButtonReleaseTime(0),
217 m_nLastButtonPressed(0),
219 m_aTextSelectionStart({0, 0, 0, 0}),
220 m_aTextSelectionEnd({0, 0, 0, 0}),
221 m_aGraphicSelection({0, 0, 0, 0}),
222 m_aCellCursor({0, 0, 0, 0}),
223 m_bInDragGraphicSelection(false),
224 m_pHandleStart(nullptr),
225 m_aHandleStartRect({0, 0, 0, 0}),
226 m_bInDragStartHandle(0),
227 m_pHandleMiddle(nullptr),
228 m_aHandleMiddleRect({0, 0, 0, 0}),
229 m_bInDragMiddleHandle(false),
230 m_pHandleEnd(nullptr),
231 m_aHandleEndRect({0, 0, 0, 0}),
232 m_bInDragEndHandle(false),
235 m_eDocumentType(LOK_DOCTYPE_OTHER
),
237 m_aVisibleArea({0, 0, 0, 0}),
238 m_bVisibleAreaSet(false),
241 memset(&m_aGraphicHandleRects
, 0, sizeof(m_aGraphicHandleRects
));
242 memset(&m_bInDragGraphicHandles
, 0, sizeof(m_bInDragGraphicHandles
));
245 ~LOKDocViewPrivateImpl()
248 g_source_remove(m_nTimeoutId
);
252 /// Wrapper around LOKDocViewPrivateImpl, managed by malloc/memset/free.
253 struct _LOKDocViewPrivate
255 LOKDocViewPrivateImpl
* m_pImpl
;
257 LOKDocViewPrivateImpl
* operator->()
294 PROP_USER_PROFILE_URL
,
307 PROP_DOC_PASSWORD_TO_MODIFY
,
308 PROP_TILED_ANNOTATIONS
,
313 static guint doc_view_signals
[LAST_SIGNAL
] = { 0 };
314 static GParamSpec
*properties
[PROP_LAST
] = { nullptr };
316 static void lok_doc_view_initable_iface_init (GInitableIface
*iface
);
317 static void callbackWorker (int nType
, const char* pPayload
, void* pData
);
319 SAL_DLLPUBLIC_EXPORT GType
lok_doc_view_get_type();
321 #pragma GCC diagnostic push
322 #pragma GCC diagnostic ignored "-Wunused-function"
324 G_DEFINE_TYPE_WITH_CODE (LOKDocView
, lok_doc_view
, GTK_TYPE_DRAWING_AREA
,
325 G_ADD_PRIVATE (LOKDocView
)
326 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE
, lok_doc_view_initable_iface_init
));
328 #pragma GCC diagnostic pop
331 static LOKDocViewPrivate
& getPrivate(LOKDocView
* pDocView
)
333 LOKDocViewPrivate
* priv
= static_cast<LOKDocViewPrivate
*>(lok_doc_view_get_instance_private(pDocView
));
337 /// Helper struct used to pass the data from soffice thread -> main thread.
341 std::string m_aPayload
;
342 LOKDocView
* m_pDocView
;
344 CallbackData(int nType
, const std::string
& rPayload
, LOKDocView
* pDocView
)
346 m_aPayload(rPayload
),
347 m_pDocView(pDocView
) {}
351 payloadToSize(const char* pPayload
, long& rWidth
, long& rHeight
)
353 rWidth
= rHeight
= 0;
354 gchar
** ppCoordinates
= g_strsplit(pPayload
, ", ", 2);
355 gchar
** ppCoordinate
= ppCoordinates
;
358 rWidth
= atoi(*ppCoordinate
);
362 rHeight
= atoi(*ppCoordinate
);
363 g_strfreev(ppCoordinates
);
367 LOKPostCommand (LOKDocView
* pDocView
,
368 const gchar
* pCommand
,
369 const gchar
* pArguments
,
370 gboolean 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
));
592 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
593 std::stringstream ss
;
594 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
595 g_info("%s", ss
.str().c_str());
596 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
598 if (priv
->m_nTileSizeTwips
)
600 ss
.str(std::string());
601 ss
<< "lok::Document::setClientZoom(" << nTileSizePixels
<< ", " << nTileSizePixels
<< ", " << priv
->m_nTileSizeTwips
<< ", " << priv
->m_nTileSizeTwips
<< ")";
602 g_info("%s", ss
.str().c_str());
603 priv
->m_pDocument
->pClass
->setClientZoom(priv
->m_pDocument
,
606 priv
->m_nTileSizeTwips
,
607 priv
->m_nTileSizeTwips
);
608 priv
->m_nTileSizeTwips
= 0;
610 if (priv
->m_bVisibleAreaSet
)
612 ss
.str(std::string());
613 ss
<< "lok::Document::setClientVisibleArea(" << priv
->m_aVisibleArea
.x
<< ", " << priv
->m_aVisibleArea
.y
<< ", ";
614 ss
<< priv
->m_aVisibleArea
.width
<< ", " << priv
->m_aVisibleArea
.height
<< ")";
615 g_info("%s", ss
.str().c_str());
616 priv
->m_pDocument
->pClass
->setClientVisibleArea(priv
->m_pDocument
,
617 priv
->m_aVisibleArea
.x
,
618 priv
->m_aVisibleArea
.y
,
619 priv
->m_aVisibleArea
.width
,
620 priv
->m_aVisibleArea
.height
);
621 priv
->m_bVisibleAreaSet
= false;
624 ss
.str(std::string());
625 ss
<< "lok::Document::postKeyEvent(" << pLOEvent
->m_nKeyEvent
<< ", " << pLOEvent
->m_nCharCode
<< ", " << pLOEvent
->m_nKeyCode
<< ")";
626 g_info("%s", ss
.str().c_str());
627 priv
->m_pDocument
->pClass
->postKeyEvent(priv
->m_pDocument
,
628 pLOEvent
->m_nKeyEvent
,
629 pLOEvent
->m_nCharCode
,
630 pLOEvent
->m_nKeyCode
);
634 signalKey (GtkWidget
* pWidget
, GdkEventKey
* pEvent
)
636 LOKDocView
* pDocView
= LOK_DOC_VIEW(pWidget
);
637 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
640 GError
* error
= nullptr;
644 g_info("signalKey: not in edit mode, ignore");
648 priv
->m_nKeyModifier
&= KEY_MOD2
;
649 switch (pEvent
->keyval
)
651 case GDK_KEY_BackSpace
:
652 nKeyCode
= com::sun::star::awt::Key::BACKSPACE
;
655 nKeyCode
= com::sun::star::awt::Key::DELETE
;
658 case GDK_KEY_KP_Enter
:
659 nKeyCode
= com::sun::star::awt::Key::RETURN
;
662 nKeyCode
= com::sun::star::awt::Key::ESCAPE
;
665 nKeyCode
= com::sun::star::awt::Key::TAB
;
668 nKeyCode
= com::sun::star::awt::Key::DOWN
;
671 nKeyCode
= com::sun::star::awt::Key::UP
;
674 nKeyCode
= com::sun::star::awt::Key::LEFT
;
677 nKeyCode
= com::sun::star::awt::Key::RIGHT
;
679 case GDK_KEY_Page_Down
:
680 nKeyCode
= com::sun::star::awt::Key::PAGEDOWN
;
682 case GDK_KEY_Page_Up
:
683 nKeyCode
= com::sun::star::awt::Key::PAGEUP
;
686 nKeyCode
= com::sun::star::awt::Key::INSERT
;
688 case GDK_KEY_Shift_L
:
689 case GDK_KEY_Shift_R
:
690 if (pEvent
->type
== GDK_KEY_PRESS
)
691 priv
->m_nKeyModifier
|= KEY_SHIFT
;
693 case GDK_KEY_Control_L
:
694 case GDK_KEY_Control_R
:
695 if (pEvent
->type
== GDK_KEY_PRESS
)
696 priv
->m_nKeyModifier
|= KEY_MOD1
;
700 if (pEvent
->type
== GDK_KEY_PRESS
)
701 priv
->m_nKeyModifier
|= KEY_MOD2
;
703 priv
->m_nKeyModifier
&= ~KEY_MOD2
;
706 if (pEvent
->keyval
>= GDK_KEY_F1
&& pEvent
->keyval
<= GDK_KEY_F26
)
707 nKeyCode
= com::sun::star::awt::Key::F1
+ (pEvent
->keyval
- GDK_KEY_F1
);
709 nCharCode
= gdk_keyval_to_unicode(pEvent
->keyval
);
712 // rsc is not public API, but should be good enough for debugging purposes.
713 // If this is needed for real, then probably a new param of type
714 // css::awt::KeyModifier is needed in postKeyEvent().
715 if (pEvent
->state
& GDK_SHIFT_MASK
)
716 nKeyCode
|= KEY_SHIFT
;
718 if (pEvent
->state
& GDK_CONTROL_MASK
)
719 nKeyCode
|= KEY_MOD1
;
721 if (priv
->m_nKeyModifier
& KEY_MOD2
)
722 nKeyCode
|= KEY_MOD2
;
724 if (nKeyCode
& (KEY_SHIFT
| KEY_MOD1
| KEY_MOD2
)) {
725 if (pEvent
->keyval
>= GDK_KEY_a
&& pEvent
->keyval
<= GDK_KEY_z
)
727 nKeyCode
|= 512 + (pEvent
->keyval
- GDK_KEY_a
);
729 else if (pEvent
->keyval
>= GDK_KEY_A
&& pEvent
->keyval
<= GDK_KEY_Z
) {
730 nKeyCode
|= 512 + (pEvent
->keyval
- GDK_KEY_A
);
732 else if (pEvent
->keyval
>= GDK_KEY_0
&& pEvent
->keyval
<= GDK_KEY_9
) {
733 nKeyCode
|= 256 + (pEvent
->keyval
- GDK_KEY_0
);
737 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
738 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_KEY
);
739 pLOEvent
->m_nKeyEvent
= pEvent
->type
== GDK_KEY_RELEASE
? LOK_KEYEVENT_KEYUP
: LOK_KEYEVENT_KEYINPUT
;
740 pLOEvent
->m_nCharCode
= nCharCode
;
741 pLOEvent
->m_nKeyCode
= nKeyCode
;
742 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
743 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
744 if (error
!= nullptr)
746 g_warning("Unable to call LOK_POST_KEY: %s", error
->message
);
747 g_clear_error(&error
);
749 g_object_unref(task
);
755 handleTimeout (gpointer pData
)
757 LOKDocView
* pDocView
= LOK_DOC_VIEW (pData
);
758 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
762 if (priv
->m_bCursorOverlayVisible
)
763 priv
->m_bCursorOverlayVisible
= false;
765 priv
->m_bCursorOverlayVisible
= true;
766 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
769 return G_SOURCE_CONTINUE
;
773 commandChanged(LOKDocView
* pDocView
, const std::string
& rString
)
775 g_signal_emit(pDocView
, doc_view_signals
[COMMAND_CHANGED
], 0, rString
.c_str());
779 searchNotFound(LOKDocView
* pDocView
, const std::string
& rString
)
781 g_signal_emit(pDocView
, doc_view_signals
[SEARCH_NOT_FOUND
], 0, rString
.c_str());
784 static void searchResultCount(LOKDocView
* pDocView
, const std::string
& rString
)
786 g_signal_emit(pDocView
, doc_view_signals
[SEARCH_RESULT_COUNT
], 0, rString
.c_str());
789 static void commandResult(LOKDocView
* pDocView
, const std::string
& rString
)
791 g_signal_emit(pDocView
, doc_view_signals
[COMMAND_RESULT
], 0, rString
.c_str());
794 static void addressChanged(LOKDocView
* pDocView
, const std::string
& rString
)
796 g_signal_emit(pDocView
, doc_view_signals
[ADDRESS_CHANGED
], 0, rString
.c_str());
799 static void formulaChanged(LOKDocView
* pDocView
, const std::string
& rString
)
801 g_signal_emit(pDocView
, doc_view_signals
[FORMULA_CHANGED
], 0, rString
.c_str());
804 static void reportError(LOKDocView
* /*pDocView*/, const std::string
& rString
)
806 GtkWidget
*dialog
= gtk_message_dialog_new(nullptr,
807 GTK_DIALOG_DESTROY_WITH_PARENT
,
812 gtk_dialog_run(GTK_DIALOG(dialog
));
813 gtk_widget_destroy(dialog
);
817 setPart(LOKDocView
* pDocView
, const std::string
& rString
)
819 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
820 priv
->m_nPartId
= std::stoi(rString
);
821 g_signal_emit(pDocView
, doc_view_signals
[PART_CHANGED
], 0, priv
->m_nPartId
);
825 hyperlinkClicked(LOKDocView
* pDocView
, const std::string
& rString
)
827 g_signal_emit(pDocView
, doc_view_signals
[HYPERLINK_CLICKED
], 0, rString
.c_str());
830 /// Trigger a redraw, invoked on the main thread by other functions running in a thread.
831 static gboolean
queueDraw(gpointer pData
)
833 GtkWidget
* pWidget
= static_cast<GtkWidget
*>(pData
);
835 gtk_widget_queue_draw(pWidget
);
837 return G_SOURCE_REMOVE
;
840 /// Looks up the author string from initializeForRendering()'s rendering arguments.
841 static std::string
getAuthorRenderingArgument(LOKDocViewPrivate
& priv
)
843 std::stringstream aStream
;
844 aStream
<< priv
->m_aRenderingArguments
;
845 boost::property_tree::ptree aTree
;
846 boost::property_tree::read_json(aStream
, aTree
);
848 for (const std::pair
<std::string
, boost::property_tree::ptree
>& rPair
: aTree
)
850 if (rPair
.first
== ".uno:Author")
852 aRet
= rPair
.second
.get
<std::string
>("value");
859 /// Author string <-> View ID map
860 static std::map
<std::string
, int> g_aAuthorViews
;
862 /// Set up LOKDocView after the document is loaded, invoked on the main thread by openDocumentInThread() running in a thread.
863 static gboolean
postDocumentLoad(gpointer pData
)
865 LOKDocView
* pLOKDocView
= static_cast<LOKDocView
*>(pData
);
866 LOKDocViewPrivate
& priv
= getPrivate(pLOKDocView
);
868 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
869 priv
->m_pDocument
->pClass
->initializeForRendering(priv
->m_pDocument
, priv
->m_aRenderingArguments
.c_str());
870 priv
->m_nViewId
= priv
->m_pDocument
->pClass
->getView(priv
->m_pDocument
);
871 g_aAuthorViews
[getAuthorRenderingArgument(priv
)] = priv
->m_nViewId
;
872 priv
->m_pDocument
->pClass
->registerCallback(priv
->m_pDocument
, callbackWorker
, pLOKDocView
);
873 priv
->m_pDocument
->pClass
->getDocumentSize(priv
->m_pDocument
, &priv
->m_nDocumentWidthTwips
, &priv
->m_nDocumentHeightTwips
);
874 priv
->m_nParts
= priv
->m_pDocument
->pClass
->getParts(priv
->m_pDocument
);
876 priv
->m_nTimeoutId
= g_timeout_add(600, handleTimeout
, pLOKDocView
);
878 float zoom
= priv
->m_fZoom
;
879 long nDocumentWidthTwips
= priv
->m_nDocumentWidthTwips
;
880 long nDocumentHeightTwips
= priv
->m_nDocumentHeightTwips
;
881 long nDocumentWidthPixels
= twipToPixel(nDocumentWidthTwips
, zoom
);
882 long nDocumentHeightPixels
= twipToPixel(nDocumentHeightTwips
, zoom
);
883 // Total number of columns in this document.
884 guint nColumns
= ceil(static_cast<double>(nDocumentWidthPixels
) / nTileSizePixels
);
886 priv
->m_pTileBuffer
= std::make_unique
<TileBuffer
>(nColumns
);
887 gtk_widget_set_size_request(GTK_WIDGET(pLOKDocView
),
888 nDocumentWidthPixels
,
889 nDocumentHeightPixels
);
890 gtk_widget_set_can_focus(GTK_WIDGET(pLOKDocView
), TRUE
);
891 gtk_widget_grab_focus(GTK_WIDGET(pLOKDocView
));
892 lok_doc_view_set_zoom(pLOKDocView
, 1.0);
894 // we are completely loaded
895 priv
->m_bInit
= TRUE
;
897 return G_SOURCE_REMOVE
;
900 /// Implementation of the global callback handler, invoked by globalCallback();
902 globalCallback (gpointer pData
)
904 CallbackData
* pCallback
= static_cast<CallbackData
*>(pData
);
905 LOKDocViewPrivate
& priv
= getPrivate(pCallback
->m_pDocView
);
906 gboolean bModify
= false;
908 switch (pCallback
->m_nType
)
910 case LOK_CALLBACK_STATUS_INDICATOR_START
:
912 priv
->m_nLoadProgress
= 0.0;
913 g_signal_emit (pCallback
->m_pDocView
, doc_view_signals
[LOAD_CHANGED
], 0, 0.0);
916 case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE
:
918 priv
->m_nLoadProgress
= static_cast<gdouble
>(std::stoi(pCallback
->m_aPayload
)/100.0);
919 g_signal_emit (pCallback
->m_pDocView
, doc_view_signals
[LOAD_CHANGED
], 0, priv
->m_nLoadProgress
);
922 case LOK_CALLBACK_STATUS_INDICATOR_FINISH
:
924 priv
->m_nLoadProgress
= 1.0;
925 g_signal_emit (pCallback
->m_pDocView
, doc_view_signals
[LOAD_CHANGED
], 0, 1.0);
928 case LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY
:
931 case LOK_CALLBACK_DOCUMENT_PASSWORD
:
933 char const*const pURL(pCallback
->m_aPayload
.c_str());
934 g_signal_emit (pCallback
->m_pDocView
, doc_view_signals
[PASSWORD_REQUIRED
], 0, pURL
, bModify
);
937 case LOK_CALLBACK_ERROR
:
939 reportError(pCallback
->m_pDocView
, pCallback
->m_aPayload
);
942 case LOK_CALLBACK_SIGNATURE_STATUS
:
953 return G_SOURCE_REMOVE
;
957 globalCallbackWorker(int nType
, const char* pPayload
, void* pData
)
959 LOKDocView
* pDocView
= LOK_DOC_VIEW (pData
);
961 CallbackData
* pCallback
= new CallbackData(nType
, pPayload
? pPayload
: "(nil)", pDocView
);
962 g_info("LOKDocView_Impl::globalCallbackWorkerImpl: %s, '%s'", lokCallbackTypeToString(nType
), pPayload
);
963 gdk_threads_add_idle(globalCallback
, pCallback
);
967 payloadToRectangle (LOKDocView
* pDocView
, const char* pPayload
)
969 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
971 // x, y, width, height, part number.
972 gchar
** ppCoordinates
= g_strsplit(pPayload
, ", ", 5);
973 gchar
** ppCoordinate
= ppCoordinates
;
975 aRet
.width
= aRet
.height
= aRet
.x
= aRet
.y
= 0;
979 aRet
.x
= atoi(*ppCoordinate
);
985 aRet
.y
= atoi(*ppCoordinate
);
991 long l
= atol(*ppCoordinate
);
992 if (l
> std::numeric_limits
<int>::max())
993 aRet
.width
= std::numeric_limits
<int>::max();
996 if (aRet
.x
+ aRet
.width
> priv
->m_nDocumentWidthTwips
)
997 aRet
.width
= priv
->m_nDocumentWidthTwips
- aRet
.x
;
1001 l
= atol(*ppCoordinate
);
1002 if (l
> std::numeric_limits
<int>::max())
1003 aRet
.height
= std::numeric_limits
<int>::max();
1006 if (aRet
.y
+ aRet
.height
> priv
->m_nDocumentHeightTwips
)
1007 aRet
.height
= priv
->m_nDocumentHeightTwips
- aRet
.y
;
1008 g_strfreev(ppCoordinates
);
1013 static std::vector
<GdkRectangle
>
1014 payloadToRectangles(LOKDocView
* pDocView
, const char* pPayload
)
1016 std::vector
<GdkRectangle
> aRet
;
1018 if (g_strcmp0(pPayload
, "EMPTY") == 0)
1021 gchar
** ppRectangles
= g_strsplit(pPayload
, "; ", 0);
1022 for (gchar
** ppRectangle
= ppRectangles
; *ppRectangle
; ++ppRectangle
)
1023 aRet
.push_back(payloadToRectangle(pDocView
, *ppRectangle
));
1024 g_strfreev(ppRectangles
);
1031 setTilesInvalid (LOKDocView
* pDocView
, const GdkRectangle
& rRectangle
)
1033 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1034 GdkRectangle aRectanglePixels
;
1035 GdkPoint aStart
, aEnd
;
1037 aRectanglePixels
.x
= twipToPixel(rRectangle
.x
, priv
->m_fZoom
);
1038 aRectanglePixels
.y
= twipToPixel(rRectangle
.y
, priv
->m_fZoom
);
1039 aRectanglePixels
.width
= twipToPixel(rRectangle
.width
, priv
->m_fZoom
);
1040 aRectanglePixels
.height
= twipToPixel(rRectangle
.height
, priv
->m_fZoom
);
1042 aStart
.x
= aRectanglePixels
.y
/ nTileSizePixels
;
1043 aStart
.y
= aRectanglePixels
.x
/ nTileSizePixels
;
1044 aEnd
.x
= (aRectanglePixels
.y
+ aRectanglePixels
.height
+ nTileSizePixels
) / nTileSizePixels
;
1045 aEnd
.y
= (aRectanglePixels
.x
+ aRectanglePixels
.width
+ nTileSizePixels
) / nTileSizePixels
;
1046 for (int i
= aStart
.x
; i
< aEnd
.x
; i
++)
1048 for (int j
= aStart
.y
; j
< aEnd
.y
; j
++)
1050 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
1051 priv
->m_pTileBuffer
->setInvalid(i
, j
, priv
->m_fZoom
, task
, priv
->lokThreadPool
);
1052 g_object_unref(task
);
1058 callback (gpointer pData
)
1060 CallbackData
* pCallback
= static_cast<CallbackData
*>(pData
);
1061 LOKDocView
* pDocView
= LOK_DOC_VIEW (pCallback
->m_pDocView
);
1062 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1064 //callback registered before the widget was destroyed.
1065 //Use existence of lokThreadPool as flag it was torn down
1066 if (!priv
->lokThreadPool
)
1069 return G_SOURCE_REMOVE
;
1072 switch (pCallback
->m_nType
)
1074 case LOK_CALLBACK_INVALIDATE_TILES
:
1076 if (pCallback
->m_aPayload
.compare(0, 5, "EMPTY") != 0) // payload doesn't start with "EMPTY"
1078 GdkRectangle aRectangle
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1079 setTilesInvalid(pDocView
, aRectangle
);
1082 priv
->m_pTileBuffer
->resetAllTiles();
1084 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1087 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
:
1090 std::stringstream
aStream(pCallback
->m_aPayload
);
1091 boost::property_tree::ptree aTree
;
1092 boost::property_tree::read_json(aStream
, aTree
);
1093 const std::string
& rRectangle
= aTree
.get
<std::string
>("rectangle");
1094 int nViewId
= aTree
.get
<int>("viewId");
1096 priv
->m_aVisibleCursor
= payloadToRectangle(pDocView
, rRectangle
.c_str());
1097 priv
->m_bCursorOverlayVisible
= true;
1098 std::cerr
<< nViewId
;
1099 std::cerr
<< priv
->m_nViewId
;
1100 if(nViewId
== priv
->m_nViewId
)
1102 g_signal_emit(pDocView
, doc_view_signals
[CURSOR_CHANGED
], 0,
1103 priv
->m_aVisibleCursor
.x
,
1104 priv
->m_aVisibleCursor
.y
,
1105 priv
->m_aVisibleCursor
.width
,
1106 priv
->m_aVisibleCursor
.height
);
1108 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1111 case LOK_CALLBACK_TEXT_SELECTION
:
1113 priv
->m_aTextSelectionRectangles
= payloadToRectangles(pDocView
, pCallback
->m_aPayload
.c_str());
1114 gboolean bIsTextSelected
= !priv
->m_aTextSelectionRectangles
.empty();
1115 // In case the selection is empty, then we get no LOK_CALLBACK_TEXT_SELECTION_START/END events.
1116 if (!bIsTextSelected
)
1118 memset(&priv
->m_aTextSelectionStart
, 0, sizeof(priv
->m_aTextSelectionStart
));
1119 memset(&priv
->m_aHandleStartRect
, 0, sizeof(priv
->m_aHandleStartRect
));
1120 memset(&priv
->m_aTextSelectionEnd
, 0, sizeof(priv
->m_aTextSelectionEnd
));
1121 memset(&priv
->m_aHandleEndRect
, 0, sizeof(priv
->m_aHandleEndRect
));
1124 memset(&priv
->m_aHandleMiddleRect
, 0, sizeof(priv
->m_aHandleMiddleRect
));
1126 g_signal_emit(pDocView
, doc_view_signals
[TEXT_SELECTION
], 0, bIsTextSelected
);
1127 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1130 case LOK_CALLBACK_TEXT_SELECTION_START
:
1132 priv
->m_aTextSelectionStart
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1135 case LOK_CALLBACK_TEXT_SELECTION_END
:
1137 priv
->m_aTextSelectionEnd
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1140 case LOK_CALLBACK_CURSOR_VISIBLE
:
1142 priv
->m_bCursorVisible
= pCallback
->m_aPayload
== "true";
1145 case LOK_CALLBACK_MOUSE_POINTER
:
1147 // We do not want the cursor to get changed in view-only mode
1150 // The gtk docs claim that most css cursors should be supported, however
1151 // on my system at least this is not true and many cursors are unsupported.
1152 // In this case pCursor = null, which results in the default cursor
1154 GdkCursor
* pCursor
= gdk_cursor_new_from_name(gtk_widget_get_display(GTK_WIDGET(pDocView
)),
1155 pCallback
->m_aPayload
.c_str());
1156 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(pDocView
)), pCursor
);
1160 case LOK_CALLBACK_GRAPHIC_SELECTION
:
1162 if (pCallback
->m_aPayload
!= "EMPTY")
1163 priv
->m_aGraphicSelection
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1165 memset(&priv
->m_aGraphicSelection
, 0, sizeof(priv
->m_aGraphicSelection
));
1166 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1169 case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION
:
1171 std::stringstream
aStream(pCallback
->m_aPayload
);
1172 boost::property_tree::ptree aTree
;
1173 boost::property_tree::read_json(aStream
, aTree
);
1174 int nViewId
= aTree
.get
<int>("viewId");
1175 int nPart
= aTree
.get
<int>("part");
1176 const std::string
& rRectangle
= aTree
.get
<std::string
>("selection");
1177 if (rRectangle
!= "EMPTY")
1178 priv
->m_aGraphicViewSelections
[nViewId
] = ViewRectangle(nPart
, payloadToRectangle(pDocView
, rRectangle
.c_str()));
1181 auto it
= priv
->m_aGraphicViewSelections
.find(nViewId
);
1182 if (it
!= priv
->m_aGraphicViewSelections
.end())
1183 priv
->m_aGraphicViewSelections
.erase(it
);
1185 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1189 case LOK_CALLBACK_CELL_CURSOR
:
1191 if (pCallback
->m_aPayload
!= "EMPTY")
1192 priv
->m_aCellCursor
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1194 memset(&priv
->m_aCellCursor
, 0, sizeof(priv
->m_aCellCursor
));
1195 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1198 case LOK_CALLBACK_HYPERLINK_CLICKED
:
1200 hyperlinkClicked(pDocView
, pCallback
->m_aPayload
);
1203 case LOK_CALLBACK_STATE_CHANGED
:
1205 commandChanged(pDocView
, pCallback
->m_aPayload
);
1208 case LOK_CALLBACK_SEARCH_NOT_FOUND
:
1210 searchNotFound(pDocView
, pCallback
->m_aPayload
);
1213 case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED
:
1215 if (!pCallback
->m_aPayload
.empty())
1216 payloadToSize(pCallback
->m_aPayload
.c_str(), priv
->m_nDocumentWidthTwips
, priv
->m_nDocumentHeightTwips
);
1218 priv
->m_pDocument
->pClass
->getDocumentSize(priv
->m_pDocument
, &priv
->m_nDocumentWidthTwips
, &priv
->m_nDocumentHeightTwips
);
1220 gtk_widget_set_size_request(GTK_WIDGET(pDocView
),
1221 twipToPixel(priv
->m_nDocumentWidthTwips
, priv
->m_fZoom
),
1222 twipToPixel(priv
->m_nDocumentHeightTwips
, priv
->m_fZoom
));
1224 g_signal_emit(pDocView
, doc_view_signals
[SIZE_CHANGED
], 0, nullptr);
1227 case LOK_CALLBACK_SET_PART
:
1229 setPart(pDocView
, pCallback
->m_aPayload
);
1232 case LOK_CALLBACK_SEARCH_RESULT_SELECTION
:
1234 boost::property_tree::ptree aTree
;
1235 std::stringstream
aStream(pCallback
->m_aPayload
);
1236 boost::property_tree::read_json(aStream
, aTree
);
1237 int nCount
= aTree
.get_child("searchResultSelection").size();
1238 searchResultCount(pDocView
, std::to_string(nCount
));
1241 case LOK_CALLBACK_UNO_COMMAND_RESULT
:
1243 commandResult(pDocView
, pCallback
->m_aPayload
);
1246 case LOK_CALLBACK_CELL_ADDRESS
:
1248 addressChanged(pDocView
, pCallback
->m_aPayload
);
1251 case LOK_CALLBACK_CELL_FORMULA
:
1253 formulaChanged(pDocView
, pCallback
->m_aPayload
);
1256 case LOK_CALLBACK_ERROR
:
1258 reportError(pDocView
, pCallback
->m_aPayload
);
1261 case LOK_CALLBACK_CONTEXT_MENU
:
1263 // TODO: Implement me
1266 case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR
:
1268 std::stringstream
aStream(pCallback
->m_aPayload
);
1269 boost::property_tree::ptree aTree
;
1270 boost::property_tree::read_json(aStream
, aTree
);
1271 int nViewId
= aTree
.get
<int>("viewId");
1272 int nPart
= aTree
.get
<int>("part");
1273 const std::string
& rRectangle
= aTree
.get
<std::string
>("rectangle");
1274 priv
->m_aViewCursors
[nViewId
] = ViewRectangle(nPart
, payloadToRectangle(pDocView
, rRectangle
.c_str()));
1275 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1278 case LOK_CALLBACK_TEXT_VIEW_SELECTION
:
1280 std::stringstream
aStream(pCallback
->m_aPayload
);
1281 boost::property_tree::ptree aTree
;
1282 boost::property_tree::read_json(aStream
, aTree
);
1283 int nViewId
= aTree
.get
<int>("viewId");
1284 int nPart
= aTree
.get
<int>("part");
1285 const std::string
& rSelection
= aTree
.get
<std::string
>("selection");
1286 priv
->m_aTextViewSelectionRectangles
[nViewId
] = ViewRectangles(nPart
, payloadToRectangles(pDocView
, rSelection
.c_str()));
1287 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1290 case LOK_CALLBACK_VIEW_CURSOR_VISIBLE
:
1292 std::stringstream
aStream(pCallback
->m_aPayload
);
1293 boost::property_tree::ptree aTree
;
1294 boost::property_tree::read_json(aStream
, aTree
);
1295 int nViewId
= aTree
.get
<int>("viewId");
1296 const std::string
& rVisible
= aTree
.get
<std::string
>("visible");
1297 priv
->m_aViewCursorVisibilities
[nViewId
] = rVisible
== "true";
1298 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1302 case LOK_CALLBACK_CELL_VIEW_CURSOR
:
1304 std::stringstream
aStream(pCallback
->m_aPayload
);
1305 boost::property_tree::ptree aTree
;
1306 boost::property_tree::read_json(aStream
, aTree
);
1307 int nViewId
= aTree
.get
<int>("viewId");
1308 int nPart
= aTree
.get
<int>("part");
1309 const std::string
& rRectangle
= aTree
.get
<std::string
>("rectangle");
1310 if (rRectangle
!= "EMPTY")
1311 priv
->m_aCellViewCursors
[nViewId
] = ViewRectangle(nPart
, payloadToRectangle(pDocView
, rRectangle
.c_str()));
1314 auto it
= priv
->m_aCellViewCursors
.find(nViewId
);
1315 if (it
!= priv
->m_aCellViewCursors
.end())
1316 priv
->m_aCellViewCursors
.erase(it
);
1318 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1321 case LOK_CALLBACK_VIEW_LOCK
:
1323 std::stringstream
aStream(pCallback
->m_aPayload
);
1324 boost::property_tree::ptree aTree
;
1325 boost::property_tree::read_json(aStream
, aTree
);
1326 int nViewId
= aTree
.get
<int>("viewId");
1327 int nPart
= aTree
.get
<int>("part");
1328 const std::string
& rRectangle
= aTree
.get
<std::string
>("rectangle");
1329 if (rRectangle
!= "EMPTY")
1330 priv
->m_aViewLockRectangles
[nViewId
] = ViewRectangle(nPart
, payloadToRectangle(pDocView
, rRectangle
.c_str()));
1333 auto it
= priv
->m_aViewLockRectangles
.find(nViewId
);
1334 if (it
!= priv
->m_aViewLockRectangles
.end())
1335 priv
->m_aViewLockRectangles
.erase(it
);
1337 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1340 case LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED
:
1344 case LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED
:
1348 case LOK_CALLBACK_COMMENT
:
1349 g_signal_emit(pCallback
->m_pDocView
, doc_view_signals
[COMMENT
], 0, pCallback
->m_aPayload
.c_str());
1351 case LOK_CALLBACK_RULER_UPDATE
:
1352 g_signal_emit(pCallback
->m_pDocView
, doc_view_signals
[RULER
], 0, pCallback
->m_aPayload
.c_str());
1354 case LOK_CALLBACK_WINDOW
:
1355 g_signal_emit(pCallback
->m_pDocView
, doc_view_signals
[WINDOW
], 0, pCallback
->m_aPayload
.c_str());
1357 case LOK_CALLBACK_INVALIDATE_HEADER
:
1358 g_signal_emit(pCallback
->m_pDocView
, doc_view_signals
[INVALIDATE_HEADER
], 0, pCallback
->m_aPayload
.c_str());
1360 case LOK_CALLBACK_CLIPBOARD_CHANGED
:
1361 case LOK_CALLBACK_CONTEXT_CHANGED
:
1362 case LOK_CALLBACK_CELL_SELECTION_AREA
:
1363 case LOK_CALLBACK_CELL_AUTO_FILL_AREA
:
1364 case LOK_CALLBACK_TABLE_SELECTED
:
1372 return G_SOURCE_REMOVE
;
1375 static void callbackWorker (int nType
, const char* pPayload
, void* pData
)
1377 LOKDocView
* pDocView
= LOK_DOC_VIEW (pData
);
1379 CallbackData
* pCallback
= new CallbackData(nType
, pPayload
? pPayload
: "(nil)", pDocView
);
1380 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1381 std::stringstream ss
;
1382 ss
<< "callbackWorker, view #" << priv
->m_nViewId
<< ": " << lokCallbackTypeToString(nType
) << ", '" << (pPayload
? pPayload
: "(nil)") << "'";
1383 g_info("%s", ss
.str().c_str());
1384 gdk_threads_add_idle(callback
, pCallback
);
1388 renderHandle(LOKDocView
* pDocView
,
1390 const GdkRectangle
& rCursor
,
1391 cairo_surface_t
* pHandle
,
1392 GdkRectangle
& rRectangle
)
1394 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1395 GdkPoint aCursorBottom
;
1396 int nHandleWidth
, nHandleHeight
;
1397 double fHandleScale
;
1399 nHandleWidth
= cairo_image_surface_get_width(pHandle
);
1400 nHandleHeight
= cairo_image_surface_get_height(pHandle
);
1401 // We want to scale down the handle, so that its height is the same as the cursor caret.
1402 fHandleScale
= twipToPixel(rCursor
.height
, priv
->m_fZoom
) / nHandleHeight
;
1403 // We want the top center of the handle bitmap to be at the bottom center of the cursor rectangle.
1404 aCursorBottom
.x
= twipToPixel(rCursor
.x
, priv
->m_fZoom
) + twipToPixel(rCursor
.width
, priv
->m_fZoom
) / 2 - (nHandleWidth
* fHandleScale
) / 2;
1405 aCursorBottom
.y
= twipToPixel(rCursor
.y
, priv
->m_fZoom
) + twipToPixel(rCursor
.height
, priv
->m_fZoom
);
1407 cairo_save (pCairo
);
1408 cairo_translate(pCairo
, aCursorBottom
.x
, aCursorBottom
.y
);
1409 cairo_scale(pCairo
, fHandleScale
, fHandleScale
);
1410 cairo_set_source_surface(pCairo
, pHandle
, 0, 0);
1411 cairo_paint(pCairo
);
1412 cairo_restore (pCairo
);
1414 rRectangle
.x
= aCursorBottom
.x
;
1415 rRectangle
.y
= aCursorBottom
.y
;
1416 rRectangle
.width
= nHandleWidth
* fHandleScale
;
1417 rRectangle
.height
= nHandleHeight
* fHandleScale
;
1420 /// Renders handles around an rSelection rectangle on pCairo.
1422 renderGraphicHandle(LOKDocView
* pDocView
,
1424 const GdkRectangle
& rSelection
,
1425 const GdkRGBA
& rColor
)
1427 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1428 int nHandleWidth
= 9, nHandleHeight
= 9;
1429 GdkRectangle aSelection
;
1431 aSelection
.x
= twipToPixel(rSelection
.x
, priv
->m_fZoom
);
1432 aSelection
.y
= twipToPixel(rSelection
.y
, priv
->m_fZoom
);
1433 aSelection
.width
= twipToPixel(rSelection
.width
, priv
->m_fZoom
);
1434 aSelection
.height
= twipToPixel(rSelection
.height
, priv
->m_fZoom
);
1436 for (int i
= 0; i
< GRAPHIC_HANDLE_COUNT
; ++i
)
1438 int x
= aSelection
.x
, y
= aSelection
.y
;
1444 case 1: // top-middle
1445 x
+= aSelection
.width
/ 2;
1447 case 2: // top-right
1448 x
+= aSelection
.width
;
1450 case 3: // middle-left
1451 y
+= aSelection
.height
/ 2;
1453 case 4: // middle-right
1454 x
+= aSelection
.width
;
1455 y
+= aSelection
.height
/ 2;
1457 case 5: // bottom-left
1458 y
+= aSelection
.height
;
1460 case 6: // bottom-middle
1461 x
+= aSelection
.width
/ 2;
1462 y
+= aSelection
.height
;
1464 case 7: // bottom-right
1465 x
+= aSelection
.width
;
1466 y
+= aSelection
.height
;
1470 // Center the handle.
1471 x
-= nHandleWidth
/ 2;
1472 y
-= nHandleHeight
/ 2;
1474 priv
->m_aGraphicHandleRects
[i
].x
= x
;
1475 priv
->m_aGraphicHandleRects
[i
].y
= y
;
1476 priv
->m_aGraphicHandleRects
[i
].width
= nHandleWidth
;
1477 priv
->m_aGraphicHandleRects
[i
].height
= nHandleHeight
;
1479 cairo_set_source_rgb(pCairo
, rColor
.red
, rColor
.green
, rColor
.blue
);
1480 cairo_rectangle(pCairo
, x
, y
, nHandleWidth
, nHandleHeight
);
1485 /// Finishes the paint tile operation and returns the result, if any
1487 paintTileFinish(LOKDocView
* pDocView
, GAsyncResult
* res
, GError
**error
)
1489 GTask
* task
= G_TASK(res
);
1491 g_return_val_if_fail(LOK_IS_DOC_VIEW(pDocView
), nullptr);
1492 g_return_val_if_fail(g_task_is_valid(res
, pDocView
), nullptr);
1493 g_return_val_if_fail(error
== nullptr || *error
== nullptr, nullptr);
1495 return g_task_propagate_pointer(task
, error
);
1498 /// Callback called in the main UI thread when paintTileInThread in LOK thread has finished
1500 paintTileCallback(GObject
* sourceObject
, GAsyncResult
* res
, gpointer userData
)
1502 LOKDocView
* pDocView
= LOK_DOC_VIEW(sourceObject
);
1503 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1504 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(userData
);
1505 std::unique_ptr
<TileBuffer
>& buffer
= priv
->m_pTileBuffer
;
1506 int index
= pLOEvent
->m_nPaintTileX
* buffer
->m_nWidth
+ pLOEvent
->m_nPaintTileY
;
1510 cairo_surface_t
* pSurface
= static_cast<cairo_surface_t
*>(paintTileFinish(pDocView
, res
, &error
));
1511 if (error
!= nullptr)
1513 if (error
->domain
== LOK_TILEBUFFER_ERROR
&&
1514 error
->code
== LOK_TILEBUFFER_CHANGED
)
1515 g_info("Skipping paint tile request because corresponding"
1516 "tile buffer has been destroyed");
1518 g_warning("Unable to get painted GdkPixbuf: %s", error
->message
);
1519 g_error_free(error
);
1523 buffer
->m_mTiles
[index
].setSurface(pSurface
);
1524 buffer
->m_mTiles
[index
].valid
= true;
1525 gdk_threads_add_idle(queueDraw
, GTK_WIDGET(pDocView
));
1527 cairo_surface_destroy(pSurface
);
1532 renderDocument(LOKDocView
* pDocView
, cairo_t
* pCairo
)
1534 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1535 GdkRectangle aVisibleArea
;
1536 long nDocumentWidthPixels
= twipToPixel(priv
->m_nDocumentWidthTwips
, priv
->m_fZoom
);
1537 long nDocumentHeightPixels
= twipToPixel(priv
->m_nDocumentHeightTwips
, priv
->m_fZoom
);
1538 // Total number of rows / columns in this document.
1539 guint nRows
= ceil(static_cast<double>(nDocumentHeightPixels
) / nTileSizePixels
);
1540 guint nColumns
= ceil(static_cast<double>(nDocumentWidthPixels
) / nTileSizePixels
);
1542 gdk_cairo_get_clip_rectangle (pCairo
, &aVisibleArea
);
1543 aVisibleArea
.x
= pixelToTwip (aVisibleArea
.x
, priv
->m_fZoom
);
1544 aVisibleArea
.y
= pixelToTwip (aVisibleArea
.y
, priv
->m_fZoom
);
1545 aVisibleArea
.width
= pixelToTwip (aVisibleArea
.width
, priv
->m_fZoom
);
1546 aVisibleArea
.height
= pixelToTwip (aVisibleArea
.height
, priv
->m_fZoom
);
1548 // Render the tiles.
1549 for (guint nRow
= 0; nRow
< nRows
; ++nRow
)
1551 for (guint nColumn
= 0; nColumn
< nColumns
; ++nColumn
)
1553 GdkRectangle aTileRectangleTwips
, aTileRectanglePixels
;
1556 // Determine size of the tile: the rightmost/bottommost tiles may
1557 // be smaller, and we need the size to decide if we need to repaint.
1558 if (nColumn
== nColumns
- 1)
1559 aTileRectanglePixels
.width
= nDocumentWidthPixels
- nColumn
* nTileSizePixels
;
1561 aTileRectanglePixels
.width
= nTileSizePixels
;
1562 if (nRow
== nRows
- 1)
1563 aTileRectanglePixels
.height
= nDocumentHeightPixels
- nRow
* nTileSizePixels
;
1565 aTileRectanglePixels
.height
= nTileSizePixels
;
1567 // Determine size and position of the tile in document coordinates,
1568 // so we can decide if we can skip painting for partial rendering.
1569 aTileRectangleTwips
.x
= pixelToTwip(nTileSizePixels
, priv
->m_fZoom
) * nColumn
;
1570 aTileRectangleTwips
.y
= pixelToTwip(nTileSizePixels
, priv
->m_fZoom
) * nRow
;
1571 aTileRectangleTwips
.width
= pixelToTwip(aTileRectanglePixels
.width
, priv
->m_fZoom
);
1572 aTileRectangleTwips
.height
= pixelToTwip(aTileRectanglePixels
.height
, priv
->m_fZoom
);
1574 if (!gdk_rectangle_intersect(&aVisibleArea
, &aTileRectangleTwips
, nullptr))
1579 LOEvent
* pLOEvent
= new LOEvent(LOK_PAINT_TILE
);
1580 pLOEvent
->m_nPaintTileX
= nRow
;
1581 pLOEvent
->m_nPaintTileY
= nColumn
;
1582 pLOEvent
->m_fPaintTileZoom
= priv
->m_fZoom
;
1583 pLOEvent
->m_pTileBuffer
= &*priv
->m_pTileBuffer
;
1584 GTask
* task
= g_task_new(pDocView
, nullptr, paintTileCallback
, pLOEvent
);
1585 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
1587 Tile
& currentTile
= priv
->m_pTileBuffer
->getTile(nRow
, nColumn
, task
, priv
->lokThreadPool
);
1588 cairo_surface_t
* pSurface
= currentTile
.getBuffer();
1589 cairo_set_source_surface(pCairo
, pSurface
,
1590 twipToPixel(aTileRectangleTwips
.x
, priv
->m_fZoom
),
1591 twipToPixel(aTileRectangleTwips
.y
, priv
->m_fZoom
));
1592 cairo_paint(pCairo
);
1593 g_object_unref(task
);
1601 static const GdkRGBA
& getDarkColor(int nViewId
, LOKDocViewPrivate
& priv
)
1603 static std::map
<int, GdkRGBA
> aColorMap
;
1604 auto it
= aColorMap
.find(nViewId
);
1605 if (it
!= aColorMap
.end())
1608 if (priv
->m_eDocumentType
== LOK_DOCTYPE_TEXT
)
1610 char* pValues
= priv
->m_pDocument
->pClass
->getCommandValues(priv
->m_pDocument
, ".uno:TrackedChangeAuthors");
1611 std::stringstream aInfo
;
1612 aInfo
<< "lok::Document::getCommandValues('.uno:TrackedChangeAuthors') returned '" << pValues
<< "'" << std::endl
;
1613 g_info("%s", aInfo
.str().c_str());
1615 std::stringstream
aStream(pValues
);
1616 boost::property_tree::ptree aTree
;
1617 boost::property_tree::read_json(aStream
, aTree
);
1618 for (const auto& rValue
: aTree
.get_child("authors"))
1620 const std::string
& rName
= rValue
.second
.get
<std::string
>("name");
1621 guint32 nColor
= rValue
.second
.get
<guint32
>("color");
1622 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};
1623 auto itAuthorViews
= g_aAuthorViews
.find(rName
);
1624 if (itAuthorViews
!= g_aAuthorViews
.end())
1625 aColorMap
[itAuthorViews
->second
] = aColor
;
1630 // Based on tools/color.hxx, COL_AUTHOR1_DARK..COL_AUTHOR9_DARK.
1631 static std::vector
<GdkRGBA
> aColors
=
1633 {(double(198))/255, (double(146))/255, (double(0))/255, 0},
1634 {(double(6))/255, (double(70))/255, (double(162))/255, 0},
1635 {(double(87))/255, (double(157))/255, (double(28))/255, 0},
1636 {(double(105))/255, (double(43))/255, (double(157))/255, 0},
1637 {(double(197))/255, (double(0))/255, (double(11))/255, 0},
1638 {(double(0))/255, (double(128))/255, (double(128))/255, 0},
1639 {(double(140))/255, (double(132))/255, (double(0))/255, 0},
1640 {(double(43))/255, (double(85))/255, (double(107))/255, 0},
1641 {(double(209))/255, (double(118))/255, (double(0))/255, 0},
1643 static int nColorCounter
= 0;
1644 GdkRGBA aColor
= aColors
[nColorCounter
++ % aColors
.size()];
1645 aColorMap
[nViewId
] = aColor
;
1647 assert(aColorMap
.find(nViewId
) != aColorMap
.end());
1648 return aColorMap
[nViewId
];
1652 renderOverlay(LOKDocView
* pDocView
, cairo_t
* pCairo
)
1654 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1656 if (priv
->m_bEdit
&& priv
->m_bCursorVisible
&& priv
->m_bCursorOverlayVisible
&& !isEmptyRectangle(priv
->m_aVisibleCursor
))
1658 if (priv
->m_aVisibleCursor
.width
< 30)
1659 // Set a minimal width if it would be 0.
1660 priv
->m_aVisibleCursor
.width
= 30;
1662 cairo_set_source_rgb(pCairo
, 0, 0, 0);
1663 cairo_rectangle(pCairo
,
1664 twipToPixel(priv
->m_aVisibleCursor
.x
, priv
->m_fZoom
),
1665 twipToPixel(priv
->m_aVisibleCursor
.y
, priv
->m_fZoom
),
1666 twipToPixel(priv
->m_aVisibleCursor
.width
, priv
->m_fZoom
),
1667 twipToPixel(priv
->m_aVisibleCursor
.height
, priv
->m_fZoom
));
1671 // View cursors: they do not blink and are colored.
1672 if (priv
->m_bEdit
&& !priv
->m_aViewCursors
.empty())
1674 for (auto& rPair
: priv
->m_aViewCursors
)
1676 auto itVisibility
= priv
->m_aViewCursorVisibilities
.find(rPair
.first
);
1677 if (itVisibility
!= priv
->m_aViewCursorVisibilities
.end() && !itVisibility
->second
)
1680 // Show view cursors when in Writer or when the part matches.
1681 if (rPair
.second
.m_nPart
!= priv
->m_nPartId
&& priv
->m_eDocumentType
!= LOK_DOCTYPE_TEXT
)
1684 GdkRectangle
& rCursor
= rPair
.second
.m_aRectangle
;
1685 if (rCursor
.width
< 30)
1686 // Set a minimal width if it would be 0.
1689 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
1690 cairo_set_source_rgb(pCairo
, rDark
.red
, rDark
.green
, rDark
.blue
);
1691 cairo_rectangle(pCairo
,
1692 twipToPixel(rCursor
.x
, priv
->m_fZoom
),
1693 twipToPixel(rCursor
.y
, priv
->m_fZoom
),
1694 twipToPixel(rCursor
.width
, priv
->m_fZoom
),
1695 twipToPixel(rCursor
.height
, priv
->m_fZoom
));
1700 if (priv
->m_bEdit
&& priv
->m_bCursorVisible
&& !isEmptyRectangle(priv
->m_aVisibleCursor
) && priv
->m_aTextSelectionRectangles
.empty())
1702 // Have a cursor, but no selection: we need the middle handle.
1703 gchar
* handleMiddlePath
= g_strconcat (priv
->m_aLOPath
.c_str(), CURSOR_HANDLE_DIR
, "handle_image_middle.png", nullptr);
1704 if (!priv
->m_pHandleMiddle
)
1706 priv
->m_pHandleMiddle
= cairo_image_surface_create_from_png(handleMiddlePath
);
1707 assert(cairo_surface_status(priv
->m_pHandleMiddle
) == CAIRO_STATUS_SUCCESS
);
1709 g_free (handleMiddlePath
);
1710 renderHandle(pDocView
, pCairo
, priv
->m_aVisibleCursor
, priv
->m_pHandleMiddle
, priv
->m_aHandleMiddleRect
);
1713 if (!priv
->m_aTextSelectionRectangles
.empty())
1715 for (const GdkRectangle
& rRectangle
: priv
->m_aTextSelectionRectangles
)
1717 // Blue with 75% transparency.
1718 cairo_set_source_rgba(pCairo
, (double(0x43))/255, (double(0xac))/255, (double(0xe8))/255, 0.25);
1719 cairo_rectangle(pCairo
,
1720 twipToPixel(rRectangle
.x
, priv
->m_fZoom
),
1721 twipToPixel(rRectangle
.y
, priv
->m_fZoom
),
1722 twipToPixel(rRectangle
.width
, priv
->m_fZoom
),
1723 twipToPixel(rRectangle
.height
, priv
->m_fZoom
));
1728 if (!isEmptyRectangle(priv
->m_aTextSelectionStart
))
1730 // Have a start position: we need a start handle.
1731 gchar
* handleStartPath
= g_strconcat (priv
->m_aLOPath
.c_str(), CURSOR_HANDLE_DIR
, "handle_image_start.png", nullptr);
1732 if (!priv
->m_pHandleStart
)
1734 priv
->m_pHandleStart
= cairo_image_surface_create_from_png(handleStartPath
);
1735 assert(cairo_surface_status(priv
->m_pHandleStart
) == CAIRO_STATUS_SUCCESS
);
1737 renderHandle(pDocView
, pCairo
, priv
->m_aTextSelectionStart
, priv
->m_pHandleStart
, priv
->m_aHandleStartRect
);
1738 g_free (handleStartPath
);
1740 if (!isEmptyRectangle(priv
->m_aTextSelectionEnd
))
1742 // Have a start position: we need an end handle.
1743 gchar
* handleEndPath
= g_strconcat (priv
->m_aLOPath
.c_str(), CURSOR_HANDLE_DIR
, "handle_image_end.png", nullptr);
1744 if (!priv
->m_pHandleEnd
)
1746 priv
->m_pHandleEnd
= cairo_image_surface_create_from_png(handleEndPath
);
1747 assert(cairo_surface_status(priv
->m_pHandleEnd
) == CAIRO_STATUS_SUCCESS
);
1749 renderHandle(pDocView
, pCairo
, priv
->m_aTextSelectionEnd
, priv
->m_pHandleEnd
, priv
->m_aHandleEndRect
);
1750 g_free (handleEndPath
);
1754 // Selections of other views.
1755 for (const auto& rPair
: priv
->m_aTextViewSelectionRectangles
)
1757 if (rPair
.second
.m_nPart
!= priv
->m_nPartId
&& priv
->m_eDocumentType
!= LOK_DOCTYPE_TEXT
)
1760 for (const GdkRectangle
& rRectangle
: rPair
.second
.m_aRectangles
)
1762 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
1763 // 75% transparency.
1764 cairo_set_source_rgba(pCairo
, rDark
.red
, rDark
.green
, rDark
.blue
, 0.25);
1765 cairo_rectangle(pCairo
,
1766 twipToPixel(rRectangle
.x
, priv
->m_fZoom
),
1767 twipToPixel(rRectangle
.y
, priv
->m_fZoom
),
1768 twipToPixel(rRectangle
.width
, priv
->m_fZoom
),
1769 twipToPixel(rRectangle
.height
, priv
->m_fZoom
));
1774 if (!isEmptyRectangle(priv
->m_aGraphicSelection
))
1776 GdkRGBA
const aBlack
{0, 0, 0, 0};
1777 renderGraphicHandle(pDocView
, pCairo
, priv
->m_aGraphicSelection
, aBlack
);
1780 // Graphic selections of other views.
1781 for (const auto& rPair
: priv
->m_aGraphicViewSelections
)
1783 const ViewRectangle
& rRectangle
= rPair
.second
;
1784 if (rRectangle
.m_nPart
!= priv
->m_nPartId
&& priv
->m_eDocumentType
!= LOK_DOCTYPE_TEXT
)
1787 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
1788 renderGraphicHandle(pDocView
, pCairo
, rRectangle
.m_aRectangle
, rDark
);
1791 // Draw the cell cursor.
1792 if (!isEmptyRectangle(priv
->m_aCellCursor
))
1794 cairo_set_source_rgb(pCairo
, 0, 0, 0);
1795 cairo_rectangle(pCairo
,
1796 twipToPixel(priv
->m_aCellCursor
.x
, priv
->m_fZoom
),
1797 twipToPixel(priv
->m_aCellCursor
.y
, priv
->m_fZoom
),
1798 twipToPixel(priv
->m_aCellCursor
.width
, priv
->m_fZoom
),
1799 twipToPixel(priv
->m_aCellCursor
.height
, priv
->m_fZoom
));
1800 cairo_set_line_width(pCairo
, 2.0);
1801 cairo_stroke(pCairo
);
1804 // Cell view cursors: they are colored.
1805 for (const auto& rPair
: priv
->m_aCellViewCursors
)
1807 const ViewRectangle
& rCursor
= rPair
.second
;
1808 if (rCursor
.m_nPart
!= priv
->m_nPartId
)
1811 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
1812 cairo_set_source_rgb(pCairo
, rDark
.red
, rDark
.green
, rDark
.blue
);
1813 cairo_rectangle(pCairo
,
1814 twipToPixel(rCursor
.m_aRectangle
.x
, priv
->m_fZoom
),
1815 twipToPixel(rCursor
.m_aRectangle
.y
, priv
->m_fZoom
),
1816 twipToPixel(rCursor
.m_aRectangle
.width
, priv
->m_fZoom
),
1817 twipToPixel(rCursor
.m_aRectangle
.height
, priv
->m_fZoom
));
1818 cairo_set_line_width(pCairo
, 2.0);
1819 cairo_stroke(pCairo
);
1822 // View locks: they are colored.
1823 for (const auto& rPair
: priv
->m_aViewLockRectangles
)
1825 const ViewRectangle
& rRectangle
= rPair
.second
;
1826 if (rRectangle
.m_nPart
!= priv
->m_nPartId
)
1829 // Draw a rectangle.
1830 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
1831 cairo_set_source_rgb(pCairo
, rDark
.red
, rDark
.green
, rDark
.blue
);
1832 cairo_rectangle(pCairo
,
1833 twipToPixel(rRectangle
.m_aRectangle
.x
, priv
->m_fZoom
),
1834 twipToPixel(rRectangle
.m_aRectangle
.y
, priv
->m_fZoom
),
1835 twipToPixel(rRectangle
.m_aRectangle
.width
, priv
->m_fZoom
),
1836 twipToPixel(rRectangle
.m_aRectangle
.height
, priv
->m_fZoom
));
1837 cairo_set_line_width(pCairo
, 2.0);
1838 cairo_stroke(pCairo
);
1841 cairo_rectangle(pCairo
,
1842 twipToPixel(rRectangle
.m_aRectangle
.x
+ rRectangle
.m_aRectangle
.width
, priv
->m_fZoom
) - 25,
1843 twipToPixel(rRectangle
.m_aRectangle
.y
+ rRectangle
.m_aRectangle
.height
, priv
->m_fZoom
) - 15,
1848 twipToPixel(rRectangle
.m_aRectangle
.x
+ rRectangle
.m_aRectangle
.width
, priv
->m_fZoom
) - 15,
1849 twipToPixel(rRectangle
.m_aRectangle
.y
+ rRectangle
.m_aRectangle
.height
, priv
->m_fZoom
) - 15,
1851 180.0 * (M_PI
/180.0),
1852 360.0 * (M_PI
/180.0));
1853 cairo_stroke(pCairo
);
1860 lok_doc_view_signal_button(GtkWidget
* pWidget
, GdkEventButton
* pEvent
)
1862 LOKDocView
* pDocView
= LOK_DOC_VIEW (pWidget
);
1863 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1864 GError
* error
= nullptr;
1866 g_info("LOKDocView_Impl::signalButton: %d, %d (in twips: %d, %d)",
1867 static_cast<int>(pEvent
->x
), static_cast<int>(pEvent
->y
),
1868 static_cast<int>(pixelToTwip(pEvent
->x
, priv
->m_fZoom
)),
1869 static_cast<int>(pixelToTwip(pEvent
->y
, priv
->m_fZoom
)));
1870 gtk_widget_grab_focus(GTK_WIDGET(pDocView
));
1872 switch (pEvent
->type
)
1874 case GDK_BUTTON_PRESS
:
1876 GdkRectangle aClick
;
1877 aClick
.x
= pEvent
->x
;
1878 aClick
.y
= pEvent
->y
;
1882 if (handleTextSelectionOnButtonPress(aClick
, pDocView
))
1884 if (handleGraphicSelectionOnButtonPress(aClick
, pDocView
))
1888 if ((pEvent
->time
- priv
->m_nLastButtonPressTime
) < 250)
1890 priv
->m_nLastButtonPressTime
= pEvent
->time
;
1891 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
1892 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_MOUSE_EVENT
);
1893 pLOEvent
->m_nPostMouseEventType
= LOK_MOUSEEVENT_MOUSEBUTTONDOWN
;
1894 pLOEvent
->m_nPostMouseEventX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
1895 pLOEvent
->m_nPostMouseEventY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
1896 pLOEvent
->m_nPostMouseEventCount
= nCount
;
1897 switch (pEvent
->button
)
1900 pLOEvent
->m_nPostMouseEventButton
= MOUSE_LEFT
;
1903 pLOEvent
->m_nPostMouseEventButton
= MOUSE_MIDDLE
;
1906 pLOEvent
->m_nPostMouseEventButton
= MOUSE_RIGHT
;
1909 pLOEvent
->m_nPostMouseEventModifier
= priv
->m_nKeyModifier
;
1910 priv
->m_nLastButtonPressed
= pLOEvent
->m_nPostMouseEventButton
;
1911 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
1913 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
1914 if (error
!= nullptr)
1916 g_warning("Unable to call LOK_POST_MOUSE_EVENT: %s", error
->message
);
1917 g_clear_error(&error
);
1919 g_object_unref(task
);
1922 case GDK_BUTTON_RELEASE
:
1924 if (handleTextSelectionOnButtonRelease(pDocView
))
1926 if (handleGraphicSelectionOnButtonRelease(pDocView
, pEvent
))
1930 if ((pEvent
->time
- priv
->m_nLastButtonReleaseTime
) < 250)
1932 priv
->m_nLastButtonReleaseTime
= pEvent
->time
;
1933 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
1934 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_MOUSE_EVENT
);
1935 pLOEvent
->m_nPostMouseEventType
= LOK_MOUSEEVENT_MOUSEBUTTONUP
;
1936 pLOEvent
->m_nPostMouseEventX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
1937 pLOEvent
->m_nPostMouseEventY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
1938 pLOEvent
->m_nPostMouseEventCount
= nCount
;
1939 switch (pEvent
->button
)
1942 pLOEvent
->m_nPostMouseEventButton
= MOUSE_LEFT
;
1945 pLOEvent
->m_nPostMouseEventButton
= MOUSE_MIDDLE
;
1948 pLOEvent
->m_nPostMouseEventButton
= MOUSE_RIGHT
;
1951 pLOEvent
->m_nPostMouseEventModifier
= priv
->m_nKeyModifier
;
1952 priv
->m_nLastButtonPressed
= pLOEvent
->m_nPostMouseEventButton
;
1953 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
1955 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
1956 if (error
!= nullptr)
1958 g_warning("Unable to call LOK_POST_MOUSE_EVENT: %s", error
->message
);
1959 g_clear_error(&error
);
1961 g_object_unref(task
);
1971 getDragPoint(GdkRectangle
* pHandle
,
1972 GdkEventMotion
* pEvent
,
1975 GdkPoint aCursor
, aHandle
;
1977 // Center of the cursor rectangle: we know that it's above the handle.
1978 aCursor
.x
= pHandle
->x
+ pHandle
->width
/ 2;
1979 aCursor
.y
= pHandle
->y
- pHandle
->height
/ 2;
1980 // Center of the handle rectangle.
1981 aHandle
.x
= pHandle
->x
+ pHandle
->width
/ 2;
1982 aHandle
.y
= pHandle
->y
+ pHandle
->height
/ 2;
1983 // Our target is the original cursor position + the dragged offset.
1984 pPoint
->x
= aCursor
.x
+ (pEvent
->x
- aHandle
.x
);
1985 pPoint
->y
= aCursor
.y
+ (pEvent
->y
- aHandle
.y
);
1989 lok_doc_view_signal_motion (GtkWidget
* pWidget
, GdkEventMotion
* pEvent
)
1991 LOKDocView
* pDocView
= LOK_DOC_VIEW (pWidget
);
1992 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1994 GError
* error
= nullptr;
1996 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
1997 std::stringstream ss
;
1998 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
1999 g_info("%s", ss
.str().c_str());
2000 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2001 if (priv
->m_bInDragMiddleHandle
)
2003 g_info("lcl_signalMotion: dragging the middle handle");
2004 getDragPoint(&priv
->m_aHandleMiddleRect
, pEvent
, &aPoint
);
2005 priv
->m_pDocument
->pClass
->setTextSelection(priv
->m_pDocument
, LOK_SETTEXTSELECTION_RESET
, pixelToTwip(aPoint
.x
, priv
->m_fZoom
), pixelToTwip(aPoint
.y
, priv
->m_fZoom
));
2008 if (priv
->m_bInDragStartHandle
)
2010 g_info("lcl_signalMotion: dragging the start handle");
2011 getDragPoint(&priv
->m_aHandleStartRect
, pEvent
, &aPoint
);
2012 priv
->m_pDocument
->pClass
->setTextSelection(priv
->m_pDocument
, LOK_SETTEXTSELECTION_START
, pixelToTwip(aPoint
.x
, priv
->m_fZoom
), pixelToTwip(aPoint
.y
, priv
->m_fZoom
));
2015 if (priv
->m_bInDragEndHandle
)
2017 g_info("lcl_signalMotion: dragging the end handle");
2018 getDragPoint(&priv
->m_aHandleEndRect
, pEvent
, &aPoint
);
2019 priv
->m_pDocument
->pClass
->setTextSelection(priv
->m_pDocument
, LOK_SETTEXTSELECTION_END
, pixelToTwip(aPoint
.x
, priv
->m_fZoom
), pixelToTwip(aPoint
.y
, priv
->m_fZoom
));
2023 for (int i
= 0; i
< GRAPHIC_HANDLE_COUNT
; ++i
)
2025 if (priv
->m_bInDragGraphicHandles
[i
])
2027 g_info("lcl_signalMotion: dragging the graphic handle #%d", i
);
2031 if (priv
->m_bInDragGraphicSelection
)
2033 g_info("lcl_signalMotion: dragging the graphic selection");
2037 GdkRectangle aMotionInTwipsInTwips
;
2038 aMotionInTwipsInTwips
.x
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
2039 aMotionInTwipsInTwips
.y
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
2040 aMotionInTwipsInTwips
.width
= 1;
2041 aMotionInTwipsInTwips
.height
= 1;
2042 if (gdk_rectangle_intersect(&aMotionInTwipsInTwips
, &priv
->m_aGraphicSelection
, nullptr))
2044 g_info("lcl_signalMotion: start of drag graphic selection");
2045 priv
->m_bInDragGraphicSelection
= true;
2047 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
2048 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_GRAPHIC_SELECTION
);
2049 pLOEvent
->m_nSetGraphicSelectionType
= LOK_SETGRAPHICSELECTION_START
;
2050 pLOEvent
->m_nSetGraphicSelectionX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
2051 pLOEvent
->m_nSetGraphicSelectionY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
2052 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
2054 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
2055 if (error
!= nullptr)
2057 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error
->message
);
2058 g_clear_error(&error
);
2060 g_object_unref(task
);
2065 // Otherwise a mouse move, as on the desktop.
2067 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
2068 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_MOUSE_EVENT
);
2069 pLOEvent
->m_nPostMouseEventType
= LOK_MOUSEEVENT_MOUSEMOVE
;
2070 pLOEvent
->m_nPostMouseEventX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
2071 pLOEvent
->m_nPostMouseEventY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
2072 pLOEvent
->m_nPostMouseEventCount
= 1;
2073 pLOEvent
->m_nPostMouseEventButton
= priv
->m_nLastButtonPressed
;
2074 pLOEvent
->m_nPostMouseEventModifier
= priv
->m_nKeyModifier
;
2076 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
2078 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
2079 if (error
!= nullptr)
2081 g_warning("Unable to call LOK_MOUSEEVENT_MOUSEMOVE: %s", error
->message
);
2082 g_clear_error(&error
);
2084 g_object_unref(task
);
2090 setGraphicSelectionInThread(gpointer data
)
2092 GTask
* task
= G_TASK(data
);
2093 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2094 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2095 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2097 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2098 std::stringstream ss
;
2099 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2100 g_info("%s", ss
.str().c_str());
2101 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2102 ss
.str(std::string());
2103 ss
<< "lok::Document::setGraphicSelection(" << pLOEvent
->m_nSetGraphicSelectionType
;
2104 ss
<< ", " << pLOEvent
->m_nSetGraphicSelectionX
;
2105 ss
<< ", " << pLOEvent
->m_nSetGraphicSelectionY
<< ")";
2106 g_info("%s", ss
.str().c_str());
2107 priv
->m_pDocument
->pClass
->setGraphicSelection(priv
->m_pDocument
,
2108 pLOEvent
->m_nSetGraphicSelectionType
,
2109 pLOEvent
->m_nSetGraphicSelectionX
,
2110 pLOEvent
->m_nSetGraphicSelectionY
);
2114 setClientZoomInThread(gpointer data
)
2116 GTask
* task
= G_TASK(data
);
2117 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2118 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2119 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2121 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2122 priv
->m_pDocument
->pClass
->setClientZoom(priv
->m_pDocument
,
2123 pLOEvent
->m_nTilePixelWidth
,
2124 pLOEvent
->m_nTilePixelHeight
,
2125 pLOEvent
->m_nTileTwipWidth
,
2126 pLOEvent
->m_nTileTwipHeight
);
2130 postMouseEventInThread(gpointer data
)
2132 GTask
* task
= G_TASK(data
);
2133 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2134 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2135 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2137 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2138 std::stringstream ss
;
2139 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2140 g_info("%s", ss
.str().c_str());
2141 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2142 ss
.str(std::string());
2143 ss
<< "lok::Document::postMouseEvent(" << pLOEvent
->m_nPostMouseEventType
;
2144 ss
<< ", " << pLOEvent
->m_nPostMouseEventX
;
2145 ss
<< ", " << pLOEvent
->m_nPostMouseEventY
;
2146 ss
<< ", " << pLOEvent
->m_nPostMouseEventCount
;
2147 ss
<< ", " << pLOEvent
->m_nPostMouseEventButton
;
2148 ss
<< ", " << pLOEvent
->m_nPostMouseEventModifier
<< ")";
2149 g_info("%s", ss
.str().c_str());
2150 priv
->m_pDocument
->pClass
->postMouseEvent(priv
->m_pDocument
,
2151 pLOEvent
->m_nPostMouseEventType
,
2152 pLOEvent
->m_nPostMouseEventX
,
2153 pLOEvent
->m_nPostMouseEventY
,
2154 pLOEvent
->m_nPostMouseEventCount
,
2155 pLOEvent
->m_nPostMouseEventButton
,
2156 pLOEvent
->m_nPostMouseEventModifier
);
2160 openDocumentInThread (gpointer data
)
2162 GTask
* task
= G_TASK(data
);
2163 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2164 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2166 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2167 if ( priv
->m_pDocument
)
2169 priv
->m_pDocument
->pClass
->destroy( priv
->m_pDocument
);
2170 priv
->m_pDocument
= nullptr;
2173 priv
->m_pOffice
->pClass
->registerCallback(priv
->m_pOffice
, globalCallbackWorker
, pDocView
);
2174 priv
->m_pDocument
= priv
->m_pOffice
->pClass
->documentLoad( priv
->m_pOffice
, priv
->m_aDocPath
.c_str() );
2175 if ( !priv
->m_pDocument
)
2177 char *pError
= priv
->m_pOffice
->pClass
->getError( priv
->m_pOffice
);
2178 g_task_return_new_error(task
, g_quark_from_static_string ("LOK error"), 0, "%s", pError
);
2182 priv
->m_eDocumentType
= static_cast<LibreOfficeKitDocumentType
>(priv
->m_pDocument
->pClass
->getDocumentType(priv
->m_pDocument
));
2183 gdk_threads_add_idle(postDocumentLoad
, pDocView
);
2184 g_task_return_boolean (task
, true);
2189 setPartInThread(gpointer data
)
2191 GTask
* task
= G_TASK(data
);
2192 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2193 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2194 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2195 int nPart
= pLOEvent
->m_nPart
;
2197 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2198 std::stringstream ss
;
2199 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2200 g_info("%s", ss
.str().c_str());
2201 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2202 priv
->m_pDocument
->pClass
->setPart( priv
->m_pDocument
, nPart
);
2205 lok_doc_view_reset_view(pDocView
);
2209 setPartmodeInThread(gpointer data
)
2211 GTask
* task
= G_TASK(data
);
2212 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2213 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2214 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2215 int nPartMode
= pLOEvent
->m_nPartMode
;
2217 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2218 std::stringstream ss
;
2219 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2220 g_info("%s", ss
.str().c_str());
2221 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2222 priv
->m_pDocument
->pClass
->setPartMode( priv
->m_pDocument
, nPartMode
);
2226 setEditInThread(gpointer data
)
2228 GTask
* task
= G_TASK(data
);
2229 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2230 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2231 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2232 gboolean bWasEdit
= priv
->m_bEdit
;
2233 gboolean bEdit
= pLOEvent
->m_bEdit
;
2235 if (!priv
->m_bEdit
&& bEdit
)
2236 g_info("lok_doc_view_set_edit: entering edit mode");
2237 else if (priv
->m_bEdit
&& !bEdit
)
2239 g_info("lok_doc_view_set_edit: leaving edit mode");
2240 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2241 std::stringstream ss
;
2242 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2243 g_info("%s", ss
.str().c_str());
2244 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2245 priv
->m_pDocument
->pClass
->resetSelection(priv
->m_pDocument
);
2247 priv
->m_bEdit
= bEdit
;
2248 g_signal_emit(pDocView
, doc_view_signals
[EDIT_CHANGED
], 0, bWasEdit
);
2249 gdk_threads_add_idle(queueDraw
, GTK_WIDGET(pDocView
));
2253 postCommandInThread (gpointer data
)
2255 GTask
* task
= G_TASK(data
);
2256 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2257 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2258 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2260 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2261 std::stringstream ss
;
2262 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2263 g_info("%s", ss
.str().c_str());
2264 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2265 ss
.str(std::string());
2266 ss
<< "lok::Document::postUnoCommand(" << pLOEvent
->m_pCommand
<< ", " << pLOEvent
->m_pArguments
<< ")";
2267 g_info("%s", ss
.str().c_str());
2268 priv
->m_pDocument
->pClass
->postUnoCommand(priv
->m_pDocument
, pLOEvent
->m_pCommand
, pLOEvent
->m_pArguments
, pLOEvent
->m_bNotifyWhenFinished
);
2272 paintTileInThread (gpointer data
)
2274 GTask
* task
= G_TASK(data
);
2275 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2276 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2277 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2279 // check if "source" tile buffer is different from "current" tile buffer
2280 if (pLOEvent
->m_pTileBuffer
!= &*priv
->m_pTileBuffer
)
2282 pLOEvent
->m_pTileBuffer
= nullptr;
2283 g_task_return_new_error(task
,
2284 LOK_TILEBUFFER_ERROR
,
2285 LOK_TILEBUFFER_CHANGED
,
2286 "TileBuffer has changed");
2289 std::unique_ptr
<TileBuffer
>& buffer
= priv
->m_pTileBuffer
;
2290 int index
= pLOEvent
->m_nPaintTileX
* buffer
->m_nWidth
+ pLOEvent
->m_nPaintTileY
;
2291 if (buffer
->m_mTiles
.find(index
) != buffer
->m_mTiles
.end() &&
2292 buffer
->m_mTiles
[index
].valid
)
2295 cairo_surface_t
*pSurface
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32
, nTileSizePixels
, nTileSizePixels
);
2296 if (cairo_surface_status(pSurface
) != CAIRO_STATUS_SUCCESS
)
2298 cairo_surface_destroy(pSurface
);
2299 g_task_return_new_error(task
,
2300 LOK_TILEBUFFER_ERROR
,
2301 LOK_TILEBUFFER_MEMORY
,
2302 "Error allocating Surface");
2306 unsigned char* pBuffer
= cairo_image_surface_get_data(pSurface
);
2307 GdkRectangle aTileRectangle
;
2308 aTileRectangle
.x
= pixelToTwip(nTileSizePixels
, pLOEvent
->m_fPaintTileZoom
) * pLOEvent
->m_nPaintTileY
;
2309 aTileRectangle
.y
= pixelToTwip(nTileSizePixels
, pLOEvent
->m_fPaintTileZoom
) * pLOEvent
->m_nPaintTileX
;
2311 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2312 std::stringstream ss
;
2313 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2314 g_info("%s", ss
.str().c_str());
2315 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2316 ss
.str(std::string());
2317 GTimer
* aTimer
= g_timer_new();
2319 ss
<< "lok::Document::paintTile(" << static_cast<void*>(pBuffer
) << ", "
2320 << nTileSizePixels
<< ", " << nTileSizePixels
<< ", "
2321 << aTileRectangle
.x
<< ", " << aTileRectangle
.y
<< ", "
2322 << pixelToTwip(nTileSizePixels
, pLOEvent
->m_fPaintTileZoom
) << ", "
2323 << pixelToTwip(nTileSizePixels
, pLOEvent
->m_fPaintTileZoom
) << ")";
2325 priv
->m_pDocument
->pClass
->paintTile(priv
->m_pDocument
,
2327 nTileSizePixels
, nTileSizePixels
,
2328 aTileRectangle
.x
, aTileRectangle
.y
,
2329 pixelToTwip(nTileSizePixels
, pLOEvent
->m_fPaintTileZoom
),
2330 pixelToTwip(nTileSizePixels
, pLOEvent
->m_fPaintTileZoom
));
2333 g_timer_elapsed(aTimer
, &nElapsedMs
);
2334 ss
<< " rendered in " << (nElapsedMs
/ 1000.) << " milliseconds";
2335 g_info("%s", ss
.str().c_str());
2336 g_timer_destroy(aTimer
);
2338 cairo_surface_mark_dirty(pSurface
);
2340 // Its likely that while the tilebuffer has changed, one of the paint tile
2341 // requests has passed the previous check at start of this function, and has
2342 // rendered the tile already. We want to stop such rendered tiles from being
2343 // stored in new tile buffer.
2344 if (pLOEvent
->m_pTileBuffer
!= &*priv
->m_pTileBuffer
)
2346 pLOEvent
->m_pTileBuffer
= nullptr;
2347 g_task_return_new_error(task
,
2348 LOK_TILEBUFFER_ERROR
,
2349 LOK_TILEBUFFER_CHANGED
,
2350 "TileBuffer has changed");
2354 g_task_return_pointer(task
, pSurface
, reinterpret_cast<GDestroyNotify
>(cairo_surface_destroy
));
2359 lokThreadFunc(gpointer data
, gpointer
/*user_data*/)
2361 GTask
* task
= G_TASK(data
);
2362 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2363 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2364 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2366 switch (pLOEvent
->m_nType
)
2369 openDocumentInThread(task
);
2371 case LOK_POST_COMMAND
:
2372 postCommandInThread(task
);
2375 setEditInThread(task
);
2378 setPartInThread(task
);
2380 case LOK_SET_PARTMODE
:
2381 setPartmodeInThread(task
);
2384 // view-only/editable mode already checked during signal key signal emission
2385 postKeyEventInThread(task
);
2387 case LOK_PAINT_TILE
:
2388 paintTileInThread(task
);
2390 case LOK_POST_MOUSE_EVENT
:
2391 postMouseEventInThread(task
);
2393 case LOK_SET_GRAPHIC_SELECTION
:
2395 setGraphicSelectionInThread(task
);
2397 g_info ("LOK_SET_GRAPHIC_SELECTION: skipping graphical operation in view-only mode");
2399 case LOK_SET_CLIENT_ZOOM
:
2400 setClientZoomInThread(task
);
2404 g_object_unref(task
);
2407 static void lok_doc_view_init (LOKDocView
* pDocView
)
2409 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2410 priv
.m_pImpl
= new LOKDocViewPrivateImpl();
2412 gtk_widget_add_events(GTK_WIDGET(pDocView
),
2413 GDK_BUTTON_PRESS_MASK
2414 |GDK_BUTTON_RELEASE_MASK
2415 |GDK_BUTTON_MOTION_MASK
2417 |GDK_KEY_RELEASE_MASK
);
2419 priv
->lokThreadPool
= g_thread_pool_new(lokThreadFunc
,
2426 static void lok_doc_view_set_property (GObject
* object
, guint propId
, const GValue
*value
, GParamSpec
*pspec
)
2428 LOKDocView
* pDocView
= LOK_DOC_VIEW (object
);
2429 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2430 gboolean bDocPasswordEnabled
= priv
->m_nLOKFeatures
& LOK_FEATURE_DOCUMENT_PASSWORD
;
2431 gboolean bDocPasswordToModifyEnabled
= priv
->m_nLOKFeatures
& LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY
;
2432 gboolean bTiledAnnotationsEnabled
= !(priv
->m_nLOKFeatures
& LOK_FEATURE_NO_TILED_ANNOTATIONS
);
2437 priv
->m_aLOPath
= g_value_get_string (value
);
2439 case PROP_LO_UNIPOLL
:
2440 priv
->m_bUnipoll
= g_value_get_boolean (value
);
2442 case PROP_LO_POINTER
:
2443 priv
->m_pOffice
= static_cast<LibreOfficeKit
*>(g_value_get_pointer(value
));
2445 case PROP_USER_PROFILE_URL
:
2446 if (const gchar
* pUserProfile
= g_value_get_string(value
))
2447 priv
->m_aUserProfileURL
= pUserProfile
;
2450 priv
->m_aDocPath
= g_value_get_string (value
);
2452 case PROP_DOC_POINTER
:
2453 priv
->m_pDocument
= static_cast<LibreOfficeKitDocument
*>(g_value_get_pointer(value
));
2454 priv
->m_eDocumentType
= static_cast<LibreOfficeKitDocumentType
>(priv
->m_pDocument
->pClass
->getDocumentType(priv
->m_pDocument
));
2457 lok_doc_view_set_edit (pDocView
, g_value_get_boolean (value
));
2460 lok_doc_view_set_zoom (pDocView
, g_value_get_float (value
));
2462 case PROP_DOC_WIDTH
:
2463 priv
->m_nDocumentWidthTwips
= g_value_get_long (value
);
2465 case PROP_DOC_HEIGHT
:
2466 priv
->m_nDocumentHeightTwips
= g_value_get_long (value
);
2468 case PROP_DOC_PASSWORD
:
2469 if (g_value_get_boolean (value
) != bDocPasswordEnabled
)
2471 priv
->m_nLOKFeatures
= priv
->m_nLOKFeatures
^ LOK_FEATURE_DOCUMENT_PASSWORD
;
2472 priv
->m_pOffice
->pClass
->setOptionalFeatures(priv
->m_pOffice
, priv
->m_nLOKFeatures
);
2475 case PROP_DOC_PASSWORD_TO_MODIFY
:
2476 if ( g_value_get_boolean (value
) != bDocPasswordToModifyEnabled
)
2478 priv
->m_nLOKFeatures
= priv
->m_nLOKFeatures
^ LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY
;
2479 priv
->m_pOffice
->pClass
->setOptionalFeatures(priv
->m_pOffice
, priv
->m_nLOKFeatures
);
2482 case PROP_TILED_ANNOTATIONS
:
2483 if ( g_value_get_boolean (value
) != bTiledAnnotationsEnabled
)
2485 priv
->m_nLOKFeatures
= priv
->m_nLOKFeatures
^ LOK_FEATURE_NO_TILED_ANNOTATIONS
;
2486 priv
->m_pOffice
->pClass
->setOptionalFeatures(priv
->m_pOffice
, priv
->m_nLOKFeatures
);
2490 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, propId
, pspec
);
2494 static void lok_doc_view_get_property (GObject
* object
, guint propId
, GValue
*value
, GParamSpec
*pspec
)
2496 LOKDocView
* pDocView
= LOK_DOC_VIEW (object
);
2497 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2502 g_value_set_string (value
, priv
->m_aLOPath
.c_str());
2504 case PROP_LO_UNIPOLL
:
2505 g_value_set_boolean (value
, priv
->m_bUnipoll
);
2507 case PROP_LO_POINTER
:
2508 g_value_set_pointer(value
, priv
->m_pOffice
);
2510 case PROP_USER_PROFILE_URL
:
2511 g_value_set_string(value
, priv
->m_aUserProfileURL
.c_str());
2514 g_value_set_string (value
, priv
->m_aDocPath
.c_str());
2516 case PROP_DOC_POINTER
:
2517 g_value_set_pointer(value
, priv
->m_pDocument
);
2520 g_value_set_boolean (value
, priv
->m_bEdit
);
2522 case PROP_LOAD_PROGRESS
:
2523 g_value_set_double (value
, priv
->m_nLoadProgress
);
2526 g_value_set_float (value
, priv
->m_fZoom
);
2528 case PROP_IS_LOADING
:
2529 g_value_set_boolean (value
, priv
->m_bIsLoading
);
2531 case PROP_IS_INITIALIZED
:
2532 g_value_set_boolean (value
, priv
->m_bInit
);
2534 case PROP_DOC_WIDTH
:
2535 g_value_set_long (value
, priv
->m_nDocumentWidthTwips
);
2537 case PROP_DOC_HEIGHT
:
2538 g_value_set_long (value
, priv
->m_nDocumentHeightTwips
);
2540 case PROP_CAN_ZOOM_IN
:
2541 g_value_set_boolean (value
, priv
->m_bCanZoomIn
);
2543 case PROP_CAN_ZOOM_OUT
:
2544 g_value_set_boolean (value
, priv
->m_bCanZoomOut
);
2546 case PROP_DOC_PASSWORD
:
2547 g_value_set_boolean (value
, priv
->m_nLOKFeatures
& LOK_FEATURE_DOCUMENT_PASSWORD
);
2549 case PROP_DOC_PASSWORD_TO_MODIFY
:
2550 g_value_set_boolean (value
, priv
->m_nLOKFeatures
& LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY
);
2552 case PROP_TILED_ANNOTATIONS
:
2553 g_value_set_boolean (value
, !(priv
->m_nLOKFeatures
& LOK_FEATURE_NO_TILED_ANNOTATIONS
));
2556 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, propId
, pspec
);
2560 static gboolean
lok_doc_view_draw (GtkWidget
* pWidget
, cairo_t
* pCairo
)
2562 LOKDocView
*pDocView
= LOK_DOC_VIEW (pWidget
);
2564 renderDocument (pDocView
, pCairo
);
2565 renderOverlay (pDocView
, pCairo
);
2570 //rhbz#1444437 finalize may not occur immediately when this widget is destroyed
2571 //it may happen during GC of javascript, e.g. in gnome-documents but "destroy"
2572 //will be called promptly, so close documents in destroy, not finalize
2573 static void lok_doc_view_destroy (GtkWidget
* widget
)
2575 LOKDocView
* pDocView
= LOK_DOC_VIEW (widget
);
2576 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2578 // Ignore notifications sent to this view on shutdown.
2579 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2580 std::stringstream ss
;
2581 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2582 g_info("%s", ss
.str().c_str());
2583 if (priv
->m_pDocument
)
2585 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2586 priv
->m_pDocument
->pClass
->registerCallback(priv
->m_pDocument
, nullptr, nullptr);
2589 if (priv
->lokThreadPool
)
2591 g_thread_pool_free(priv
->lokThreadPool
, true, true);
2592 priv
->lokThreadPool
= nullptr;
2597 if (priv
->m_pDocument
)
2599 if (priv
->m_pDocument
->pClass
->getViewsCount(priv
->m_pDocument
) > 1)
2601 priv
->m_pDocument
->pClass
->destroyView(priv
->m_pDocument
, priv
->m_nViewId
);
2605 if (priv
->m_pDocument
)
2607 priv
->m_pDocument
->pClass
->destroy (priv
->m_pDocument
);
2608 priv
->m_pDocument
= nullptr;
2610 if (priv
->m_pOffice
)
2612 priv
->m_pOffice
->pClass
->destroy (priv
->m_pOffice
);
2613 priv
->m_pOffice
= nullptr;
2618 GTK_WIDGET_CLASS (lok_doc_view_parent_class
)->destroy (widget
);
2621 static void lok_doc_view_finalize (GObject
* object
)
2623 LOKDocView
* pDocView
= LOK_DOC_VIEW (object
);
2624 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2626 delete priv
.m_pImpl
;
2627 priv
.m_pImpl
= nullptr;
2629 G_OBJECT_CLASS (lok_doc_view_parent_class
)->finalize (object
);
2632 // kicks the mainloop awake
2633 static gboolean
timeout_wakeup(void *)
2638 // integrate our mainloop with LOK's
2639 static int lok_poll_callback(void*, int timeoutUs
)
2643 guint timeout
= g_timeout_add(timeoutUs
/ 1000, timeout_wakeup
, nullptr);
2644 g_main_context_iteration(nullptr, TRUE
);
2645 g_source_remove(timeout
);
2648 g_main_context_iteration(nullptr, FALSE
);
2653 // thread-safe wakeup of our mainloop
2654 static void lok_wake_callback(void *)
2656 g_main_context_wakeup(nullptr);
2659 static gboolean
spin_lok_loop(void *pData
)
2661 LOKDocView
*pDocView
= LOK_DOC_VIEW (pData
);
2662 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2663 priv
->m_pOffice
->pClass
->runLoop(priv
->m_pOffice
, lok_poll_callback
, lok_wake_callback
, nullptr);
2667 static gboolean
lok_doc_view_initable_init (GInitable
*initable
, GCancellable
* /*cancellable*/, GError
**error
)
2669 LOKDocView
*pDocView
= LOK_DOC_VIEW (initable
);
2670 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2672 if (priv
->m_pOffice
!= nullptr)
2675 if (priv
->m_bUnipoll
)
2676 g_setenv("SAL_LOK_OPTIONS", "unipoll", FALSE
);
2678 priv
->m_pOffice
= lok_init_2(priv
->m_aLOPath
.c_str(), priv
->m_aUserProfileURL
.empty() ? nullptr : priv
->m_aUserProfileURL
.c_str());
2680 if (priv
->m_pOffice
== nullptr)
2683 g_quark_from_static_string ("LOK initialization error"), 0,
2684 "Failed to get LibreOfficeKit context. Make sure path (%s) is correct",
2685 priv
->m_aLOPath
.c_str());
2688 priv
->m_nLOKFeatures
|= LOK_FEATURE_PART_IN_INVALIDATION_CALLBACK
;
2689 priv
->m_nLOKFeatures
|= LOK_FEATURE_VIEWID_IN_VISCURSOR_INVALIDATION_CALLBACK
;
2690 priv
->m_pOffice
->pClass
->setOptionalFeatures(priv
->m_pOffice
, priv
->m_nLOKFeatures
);
2692 if (priv
->m_bUnipoll
)
2693 g_idle_add(spin_lok_loop
, pDocView
);
2698 static void lok_doc_view_initable_iface_init (GInitableIface
*iface
)
2700 iface
->init
= lok_doc_view_initable_init
;
2703 static void lok_doc_view_class_init (LOKDocViewClass
* pClass
)
2705 GObjectClass
*pGObjectClass
= G_OBJECT_CLASS(pClass
);
2706 GtkWidgetClass
*pWidgetClass
= GTK_WIDGET_CLASS(pClass
);
2708 pGObjectClass
->get_property
= lok_doc_view_get_property
;
2709 pGObjectClass
->set_property
= lok_doc_view_set_property
;
2710 pGObjectClass
->finalize
= lok_doc_view_finalize
;
2712 pWidgetClass
->draw
= lok_doc_view_draw
;
2713 pWidgetClass
->button_press_event
= lok_doc_view_signal_button
;
2714 pWidgetClass
->button_release_event
= lok_doc_view_signal_button
;
2715 pWidgetClass
->key_press_event
= signalKey
;
2716 pWidgetClass
->key_release_event
= signalKey
;
2717 pWidgetClass
->motion_notify_event
= lok_doc_view_signal_motion
;
2718 pWidgetClass
->destroy
= lok_doc_view_destroy
;
2721 * LOKDocView:lopath:
2723 * The absolute path of the LibreOffice install.
2725 properties
[PROP_LO_PATH
] =
2726 g_param_spec_string("lopath",
2728 "LibreOffice Install Path",
2730 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2731 G_PARAM_CONSTRUCT_ONLY
|
2732 G_PARAM_STATIC_STRINGS
));
2735 * LOKDocView:unipoll:
2737 * Whether we use our own unified polling mainloop in place of glib's
2739 properties
[PROP_LO_UNIPOLL
] =
2740 g_param_spec_boolean("unipoll",
2742 "Whether we use a custom unified polling loop",
2744 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2745 G_PARAM_CONSTRUCT_ONLY
|
2746 G_PARAM_STATIC_STRINGS
));
2748 * LOKDocView:lopointer:
2750 * A LibreOfficeKit* in case lok_init() is already called
2753 properties
[PROP_LO_POINTER
] =
2754 g_param_spec_pointer("lopointer",
2756 "A LibreOfficeKit* from lok_init()",
2757 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2758 G_PARAM_CONSTRUCT_ONLY
|
2759 G_PARAM_STATIC_STRINGS
));
2762 * LOKDocView:userprofileurl:
2764 * The absolute path of the LibreOffice user profile.
2766 properties
[PROP_USER_PROFILE_URL
] =
2767 g_param_spec_string("userprofileurl",
2768 "User profile path",
2769 "LibreOffice user profile path",
2771 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2772 G_PARAM_CONSTRUCT_ONLY
|
2773 G_PARAM_STATIC_STRINGS
));
2776 * LOKDocView:docpath:
2778 * The path of the document that is currently being viewed.
2780 properties
[PROP_DOC_PATH
] =
2781 g_param_spec_string("docpath",
2783 "The URI of the document to open",
2785 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2786 G_PARAM_STATIC_STRINGS
));
2789 * LOKDocView:docpointer:
2791 * A LibreOfficeKitDocument* in case documentLoad() is already called
2794 properties
[PROP_DOC_POINTER
] =
2795 g_param_spec_pointer("docpointer",
2797 "A LibreOfficeKitDocument* from documentLoad()",
2798 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2799 G_PARAM_STATIC_STRINGS
));
2802 * LOKDocView:editable:
2804 * Whether the document loaded inside of #LOKDocView is editable or not.
2806 properties
[PROP_EDITABLE
] =
2807 g_param_spec_boolean("editable",
2809 "Whether the content is in edit mode or not",
2811 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2812 G_PARAM_STATIC_STRINGS
));
2815 * LOKDocView:load-progress:
2817 * The percent completion of the current loading operation of the
2818 * document. This can be used for progress bars. Note that this is not a
2819 * very accurate progress indicator, and its value might reset it couple of
2820 * times to 0 and start again. You should not rely on its numbers.
2822 properties
[PROP_LOAD_PROGRESS
] =
2823 g_param_spec_double("load-progress",
2824 "Estimated Load Progress",
2825 "Shows the progress of the document load operation",
2827 static_cast<GParamFlags
>(G_PARAM_READABLE
|
2828 G_PARAM_STATIC_STRINGS
));
2831 * LOKDocView:zoom-level:
2833 * The current zoom level of the document loaded inside #LOKDocView. The
2834 * default value is 1.0.
2836 properties
[PROP_ZOOM
] =
2837 g_param_spec_float("zoom-level",
2839 "The current zoom level of the content",
2841 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2842 G_PARAM_STATIC_STRINGS
));
2845 * LOKDocView:is-loading:
2847 * Whether the requested document is being loaded or not. %TRUE if it is
2848 * being loaded, otherwise %FALSE.
2850 properties
[PROP_IS_LOADING
] =
2851 g_param_spec_boolean("is-loading",
2853 "Whether the view is loading a document",
2855 static_cast<GParamFlags
>(G_PARAM_READABLE
|
2856 G_PARAM_STATIC_STRINGS
));
2859 * LOKDocView:is-initialized:
2861 * Whether the requested document has completely loaded or not.
2863 properties
[PROP_IS_INITIALIZED
] =
2864 g_param_spec_boolean("is-initialized",
2866 "Whether the view has completely initialized",
2868 static_cast<GParamFlags
>(G_PARAM_READABLE
|
2869 G_PARAM_STATIC_STRINGS
));
2872 * LOKDocView:doc-width:
2874 * The width of the currently loaded document in #LOKDocView in twips.
2876 properties
[PROP_DOC_WIDTH
] =
2877 g_param_spec_long("doc-width",
2879 "Width of the document in twips",
2881 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2882 G_PARAM_STATIC_STRINGS
));
2885 * LOKDocView:doc-height:
2887 * The height of the currently loaded document in #LOKDocView in twips.
2889 properties
[PROP_DOC_HEIGHT
] =
2890 g_param_spec_long("doc-height",
2892 "Height of the document in twips",
2894 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2895 G_PARAM_STATIC_STRINGS
));
2898 * LOKDocView:can-zoom-in:
2900 * It tells whether the view can further be zoomed in or not.
2902 properties
[PROP_CAN_ZOOM_IN
] =
2903 g_param_spec_boolean("can-zoom-in",
2905 "Whether the view can be zoomed in further",
2907 static_cast<GParamFlags
>(G_PARAM_READABLE
2908 | G_PARAM_STATIC_STRINGS
));
2911 * LOKDocView:can-zoom-out:
2913 * It tells whether the view can further be zoomed out or not.
2915 properties
[PROP_CAN_ZOOM_OUT
] =
2916 g_param_spec_boolean("can-zoom-out",
2918 "Whether the view can be zoomed out further",
2920 static_cast<GParamFlags
>(G_PARAM_READABLE
2921 | G_PARAM_STATIC_STRINGS
));
2924 * LOKDocView:doc-password:
2926 * Set it to true if client supports providing password for viewing
2927 * password protected documents
2929 properties
[PROP_DOC_PASSWORD
] =
2930 g_param_spec_boolean("doc-password",
2931 "Document password capability",
2932 "Whether client supports providing document passwords",
2934 static_cast<GParamFlags
>(G_PARAM_READWRITE
2935 | G_PARAM_STATIC_STRINGS
));
2938 * LOKDocView:doc-password-to-modify:
2940 * Set it to true if client supports providing password for edit-protected documents
2942 properties
[PROP_DOC_PASSWORD_TO_MODIFY
] =
2943 g_param_spec_boolean("doc-password-to-modify",
2944 "Edit document password capability",
2945 "Whether the client supports providing passwords to edit documents",
2947 static_cast<GParamFlags
>(G_PARAM_READWRITE
2948 | G_PARAM_STATIC_STRINGS
));
2951 * LOKDocView:tiled-annotations-rendering:
2953 * Set it to false if client does not want LO to render comments in tiles and
2954 * instead interested in using comments API to access comments
2956 properties
[PROP_TILED_ANNOTATIONS
] =
2957 g_param_spec_boolean("tiled-annotations",
2958 "Render comments in tiles",
2959 "Whether the client wants in tile comment rendering",
2961 static_cast<GParamFlags
>(G_PARAM_READWRITE
2962 | G_PARAM_STATIC_STRINGS
));
2964 g_object_class_install_properties(pGObjectClass
, PROP_LAST
, properties
);
2967 * LOKDocView::load-changed:
2968 * @pDocView: the #LOKDocView on which the signal is emitted
2969 * @fLoadProgress: the new progress value
2971 doc_view_signals
[LOAD_CHANGED
] =
2972 g_signal_new("load-changed",
2973 G_TYPE_FROM_CLASS (pGObjectClass
),
2977 g_cclosure_marshal_VOID__DOUBLE
,
2982 * LOKDocView::edit-changed:
2983 * @pDocView: the #LOKDocView on which the signal is emitted
2984 * @bEdit: the new edit value of the view
2986 doc_view_signals
[EDIT_CHANGED
] =
2987 g_signal_new("edit-changed",
2988 G_TYPE_FROM_CLASS (pGObjectClass
),
2992 g_cclosure_marshal_VOID__BOOLEAN
,
2997 * LOKDocView::command-changed:
2998 * @pDocView: the #LOKDocView on which the signal is emitted
2999 * @aCommand: the command that was changed
3001 doc_view_signals
[COMMAND_CHANGED
] =
3002 g_signal_new("command-changed",
3003 G_TYPE_FROM_CLASS(pGObjectClass
),
3007 g_cclosure_marshal_VOID__STRING
,
3012 * LOKDocView::search-not-found:
3013 * @pDocView: the #LOKDocView on which the signal is emitted
3014 * @aCommand: the string for which the search was not found.
3016 doc_view_signals
[SEARCH_NOT_FOUND
] =
3017 g_signal_new("search-not-found",
3018 G_TYPE_FROM_CLASS(pGObjectClass
),
3022 g_cclosure_marshal_VOID__STRING
,
3027 * LOKDocView::part-changed:
3028 * @pDocView: the #LOKDocView on which the signal is emitted
3029 * @aCommand: the part number which the view changed to
3031 doc_view_signals
[PART_CHANGED
] =
3032 g_signal_new("part-changed",
3033 G_TYPE_FROM_CLASS(pGObjectClass
),
3037 g_cclosure_marshal_VOID__INT
,
3042 * LOKDocView::size-changed:
3043 * @pDocView: the #LOKDocView on which the signal is emitted
3044 * @aCommand: NULL, we just notify that want to notify the UI elements that are interested.
3046 doc_view_signals
[SIZE_CHANGED
] =
3047 g_signal_new("size-changed",
3048 G_TYPE_FROM_CLASS(pGObjectClass
),
3052 g_cclosure_marshal_VOID__VOID
,
3057 * LOKDocView::hyperlinked-clicked:
3058 * @pDocView: the #LOKDocView on which the signal is emitted
3059 * @aHyperlink: the URI which the application should handle
3061 doc_view_signals
[HYPERLINK_CLICKED
] =
3062 g_signal_new("hyperlink-clicked",
3063 G_TYPE_FROM_CLASS(pGObjectClass
),
3067 g_cclosure_marshal_VOID__STRING
,
3072 * LOKDocView::cursor-changed:
3073 * @pDocView: the #LOKDocView on which the signal is emitted
3074 * @nX: The new cursor position (X coordinate) in pixels
3075 * @nY: The new cursor position (Y coordinate) in pixels
3076 * @nWidth: The width of new cursor
3077 * @nHeight: The height of new cursor
3079 doc_view_signals
[CURSOR_CHANGED
] =
3080 g_signal_new("cursor-changed",
3081 G_TYPE_FROM_CLASS(pGObjectClass
),
3085 g_cclosure_marshal_generic
,
3087 G_TYPE_INT
, G_TYPE_INT
,
3088 G_TYPE_INT
, G_TYPE_INT
);
3091 * LOKDocView::search-result-count:
3092 * @pDocView: the #LOKDocView on which the signal is emitted
3093 * @aCommand: number of matches.
3095 doc_view_signals
[SEARCH_RESULT_COUNT
] =
3096 g_signal_new("search-result-count",
3097 G_TYPE_FROM_CLASS(pGObjectClass
),
3101 g_cclosure_marshal_VOID__STRING
,
3106 * LOKDocView::command-result:
3107 * @pDocView: the #LOKDocView on which the signal is emitted
3108 * @aCommand: JSON containing the info about the command that finished,
3109 * and its success status.
3111 doc_view_signals
[COMMAND_RESULT
] =
3112 g_signal_new("command-result",
3113 G_TYPE_FROM_CLASS(pGObjectClass
),
3117 g_cclosure_marshal_VOID__STRING
,
3122 * LOKDocView::address-changed:
3123 * @pDocView: the #LOKDocView on which the signal is emitted
3124 * @aCommand: formula text content
3126 doc_view_signals
[ADDRESS_CHANGED
] =
3127 g_signal_new("address-changed",
3128 G_TYPE_FROM_CLASS(pGObjectClass
),
3132 g_cclosure_marshal_VOID__STRING
,
3137 * LOKDocView::formula-changed:
3138 * @pDocView: the #LOKDocView on which the signal is emitted
3139 * @aCommand: formula text content
3141 doc_view_signals
[FORMULA_CHANGED
] =
3142 g_signal_new("formula-changed",
3143 G_TYPE_FROM_CLASS(pGObjectClass
),
3147 g_cclosure_marshal_VOID__STRING
,
3152 * LOKDocView::text-selection:
3153 * @pDocView: the #LOKDocView on which the signal is emitted
3154 * @bIsTextSelected: whether text selected is non-null
3156 doc_view_signals
[TEXT_SELECTION
] =
3157 g_signal_new("text-selection",
3158 G_TYPE_FROM_CLASS(pGObjectClass
),
3162 g_cclosure_marshal_VOID__BOOLEAN
,
3167 * LOKDocView::password-required:
3168 * @pDocView: the #LOKDocView on which the signal is emitted
3169 * @pUrl: URL of the document for which password is required
3170 * @bModify: whether password id required to modify the document
3171 * This is true when password is required to edit the document,
3172 * while it can still be viewed without password. In such cases, provide a NULL
3173 * password for read-only access to the document.
3174 * If false, password is required for opening the document, and document
3175 * cannot be opened without providing a valid password.
3177 * Password must be provided by calling lok_doc_view_set_document_password
3178 * function with pUrl as provided by the callback.
3180 * Upon entering an invalid password, another `password-required` signal is
3182 * Upon entering a valid password, document starts to load.
3183 * Upon entering a NULL password: if bModify is %TRUE, document starts to
3184 * open in view-only mode, else loading of document is aborted.
3186 doc_view_signals
[PASSWORD_REQUIRED
] =
3187 g_signal_new("password-required",
3188 G_TYPE_FROM_CLASS(pGObjectClass
),
3192 g_cclosure_marshal_generic
,
3198 * LOKDocView::comment:
3199 * @pDocView: the #LOKDocView on which the signal is emitted
3200 * @pComment: the JSON string containing comment notification
3201 * The has following structure containing the information telling whether
3202 * the comment has been added, deleted or modified.
3209 * "author": "Unknown Author",
3210 * "text": "This is a comment",
3211 * "dateTime": "2016-08-18T13:13:00",
3212 * "anchorPos": "4529, 3906",
3213 * "textRange": "1418, 3906, 3111, 919"
3216 * 'action' can be 'Add', 'Remove' or 'Modify' depending on whether
3217 * comment has been added, removed or modified.
3218 * 'parent' is a non-zero comment id if this comment is a reply comment,
3219 * otherwise it's a root comment.
3221 doc_view_signals
[COMMENT
] =
3222 g_signal_new("comment",
3223 G_TYPE_FROM_CLASS(pGObjectClass
),
3227 g_cclosure_marshal_generic
,
3232 * LOKDocView::ruler:
3233 * @pDocView: the #LOKDocView on which the signal is emitted
3234 * @pPayload: the JSON string containing the information about ruler properties
3236 * The payload format is:
3241 * "leftOffset": "...",
3242 * "pageOffset": "...",
3243 * "pageWidth": "...",
3247 doc_view_signals
[RULER
] =
3248 g_signal_new("ruler",
3249 G_TYPE_FROM_CLASS(pGObjectClass
),
3253 g_cclosure_marshal_generic
,
3258 * LOKDocView::window::
3259 * @pDocView: the #LOKDocView on which the signal is emitted
3260 * @pPayload: the JSON string containing the information about the window
3262 * This signal emits information about external windows like dialogs, autopopups for now.
3264 * The payload format of pPayload is:
3267 * "id": "unique integer id of the dialog",
3268 * "action": "<see below>",
3269 * "type": "<see below>"
3270 * "rectangle": "x, y, width, height"
3273 * "type" tells the type of the window the action is associated with
3274 * - "dialog" - window is a dialog
3275 * - "child" - window is a floating window (combo boxes, etc.)
3277 * "action" can take following values:
3278 * - "created" - window is created in the backend, client can render it now
3279 * - "title_changed" - window's title is changed
3280 * - "size_changed" - window's size is changed
3281 * - "invalidate" - the area as described by "rectangle" is invalidated
3282 * Clients must request the new area
3283 * - "cursor_invalidate" - cursor is invalidated. New position is in "rectangle"
3284 * - "cursor_visible" - cursor visible status is changed. Status is available
3285 * in "visible" field
3286 * - "close" - window is closed
3288 doc_view_signals
[WINDOW
] =
3289 g_signal_new("window",
3290 G_TYPE_FROM_CLASS(pGObjectClass
),
3294 g_cclosure_marshal_generic
,
3299 * LOKDocView::invalidate-header::
3300 * @pDocView: the #LOKDocView on which the signal is emitted
3301 * @pPayload: can be either "row", "column", or "all".
3303 * The column/row header is no more valid because of a column/row insertion
3304 * or a similar event. Clients must query a new column/row header set.
3306 * The payload says if we are invalidating a row or column header
3308 doc_view_signals
[INVALIDATE_HEADER
] =
3309 g_signal_new("invalidate-header",
3310 G_TYPE_FROM_CLASS(pGObjectClass
),
3314 g_cclosure_marshal_generic
,
3319 SAL_DLLPUBLIC_EXPORT GtkWidget
*
3320 lok_doc_view_new (const gchar
* pPath
, GCancellable
*cancellable
, GError
**error
)
3322 return GTK_WIDGET (g_initable_new (LOK_TYPE_DOC_VIEW
, cancellable
, error
,
3323 "lopath", pPath
== nullptr ? LOK_PATH
: pPath
,
3324 "halign", GTK_ALIGN_CENTER
,
3325 "valign", GTK_ALIGN_CENTER
,
3329 SAL_DLLPUBLIC_EXPORT GtkWidget
*
3330 lok_doc_view_new_from_user_profile (const gchar
* pPath
, const gchar
* pUserProfile
, GCancellable
*cancellable
, GError
**error
)
3332 return GTK_WIDGET(g_initable_new(LOK_TYPE_DOC_VIEW
, cancellable
, error
,
3333 "lopath", pPath
== nullptr ? LOK_PATH
: pPath
,
3334 "userprofileurl", pUserProfile
,
3335 "halign", GTK_ALIGN_CENTER
,
3336 "valign", GTK_ALIGN_CENTER
,
3340 SAL_DLLPUBLIC_EXPORT GtkWidget
* lok_doc_view_new_from_widget(LOKDocView
* pOldLOKDocView
,
3341 const gchar
* pRenderingArguments
)
3343 LOKDocViewPrivate
& pOldPriv
= getPrivate(pOldLOKDocView
);
3344 GtkWidget
* pNewDocView
= GTK_WIDGET(g_initable_new(LOK_TYPE_DOC_VIEW
, /*cancellable=*/nullptr, /*error=*/nullptr,
3345 "lopath", pOldPriv
->m_aLOPath
.c_str(),
3346 "userprofileurl", pOldPriv
->m_aUserProfileURL
.c_str(),
3347 "lopointer", pOldPriv
->m_pOffice
,
3348 "docpointer", pOldPriv
->m_pDocument
,
3349 "halign", GTK_ALIGN_CENTER
,
3350 "valign", GTK_ALIGN_CENTER
,
3353 // No documentLoad(), just a createView().
3354 LibreOfficeKitDocument
* pDocument
= lok_doc_view_get_document(LOK_DOC_VIEW(pNewDocView
));
3355 LOKDocViewPrivate
& pNewPriv
= getPrivate(LOK_DOC_VIEW(pNewDocView
));
3356 // Store the view id only later in postDocumentLoad(), as
3357 // initializeForRendering() changes the id in Impress.
3358 pDocument
->pClass
->createView(pDocument
);
3359 pNewPriv
->m_aRenderingArguments
= pRenderingArguments
;
3361 postDocumentLoad(pNewDocView
);
3365 SAL_DLLPUBLIC_EXPORT gboolean
3366 lok_doc_view_open_document_finish (LOKDocView
* pDocView
, GAsyncResult
* res
, GError
** error
)
3368 GTask
* task
= G_TASK(res
);
3370 g_return_val_if_fail(g_task_is_valid(res
, pDocView
), false);
3371 g_return_val_if_fail(g_task_get_source_tag(task
) == lok_doc_view_open_document
, false);
3372 g_return_val_if_fail(error
== nullptr || *error
== nullptr, false);
3374 return g_task_propagate_boolean(task
, error
);
3377 SAL_DLLPUBLIC_EXPORT
void
3378 lok_doc_view_open_document (LOKDocView
* pDocView
,
3380 const gchar
* pRenderingArguments
,
3381 GCancellable
* cancellable
,
3382 GAsyncReadyCallback callback
,
3385 GTask
* task
= g_task_new(pDocView
, cancellable
, callback
, userdata
);
3386 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3387 GError
* error
= nullptr;
3389 LOEvent
* pLOEvent
= new LOEvent(LOK_LOAD_DOC
);
3391 g_object_set(G_OBJECT(pDocView
), "docpath", pPath
, nullptr);
3392 if (pRenderingArguments
)
3393 priv
->m_aRenderingArguments
= pRenderingArguments
;
3394 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
3395 g_task_set_source_tag(task
, reinterpret_cast<gpointer
>(lok_doc_view_open_document
));
3397 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
3398 if (error
!= nullptr)
3400 g_warning("Unable to call LOK_LOAD_DOC: %s", error
->message
);
3401 g_clear_error(&error
);
3403 g_object_unref(task
);
3406 SAL_DLLPUBLIC_EXPORT LibreOfficeKitDocument
*
3407 lok_doc_view_get_document (LOKDocView
* pDocView
)
3409 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3410 return priv
->m_pDocument
;
3413 SAL_DLLPUBLIC_EXPORT
void
3414 lok_doc_view_set_visible_area (LOKDocView
* pDocView
, GdkRectangle
* pVisibleArea
)
3419 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3420 priv
->m_aVisibleArea
= *pVisibleArea
;
3421 priv
->m_bVisibleAreaSet
= true;
3425 // This used to be rtl::math::approxEqual() but since that isn't inline anymore
3426 // in rtl/math.hxx and was moved into libuno_sal as rtl_math_approxEqual() to
3427 // cater for representable integer cases and we don't want to link against
3428 // libuno_sal, we'll have to have an own implementation. The special large
3429 // integer cases seems not be needed here.
3430 bool lok_approxEqual(double a
, double b
)
3432 static const double e48
= 1.0 / (16777216.0 * 16777216.0);
3435 if (a
== 0.0 || b
== 0.0)
3437 const double d
= fabs(a
- b
);
3438 return (d
< fabs(a
) * e48
&& d
< fabs(b
) * e48
);
3442 SAL_DLLPUBLIC_EXPORT
void
3443 lok_doc_view_set_zoom (LOKDocView
* pDocView
, float fZoom
)
3445 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3446 GError
* error
= nullptr;
3448 if (!priv
->m_pDocument
)
3451 // Clamp the input value in [MIN_ZOOM, MAX_ZOOM]
3452 fZoom
= fZoom
< MIN_ZOOM
? MIN_ZOOM
: fZoom
;
3453 fZoom
= std::min(fZoom
, MAX_ZOOM
);
3455 if (lok_approxEqual(fZoom
, priv
->m_fZoom
))
3458 priv
->m_fZoom
= fZoom
;
3459 long nDocumentWidthPixels
= twipToPixel(priv
->m_nDocumentWidthTwips
, fZoom
);
3460 long nDocumentHeightPixels
= twipToPixel(priv
->m_nDocumentHeightTwips
, fZoom
);
3461 // Total number of columns in this document.
3462 guint nColumns
= ceil(static_cast<double>(nDocumentWidthPixels
) / nTileSizePixels
);
3464 priv
->m_pTileBuffer
= std::make_unique
<TileBuffer
>(nColumns
);
3465 gtk_widget_set_size_request(GTK_WIDGET(pDocView
),
3466 nDocumentWidthPixels
,
3467 nDocumentHeightPixels
);
3469 g_object_notify_by_pspec(G_OBJECT(pDocView
), properties
[PROP_ZOOM
]);
3471 // set properties to indicate if view can be further zoomed in/out
3472 bool bCanZoomIn
= priv
->m_fZoom
< MAX_ZOOM
;
3473 bool bCanZoomOut
= priv
->m_fZoom
> MIN_ZOOM
;
3474 if (bCanZoomIn
!= bool(priv
->m_bCanZoomIn
))
3476 priv
->m_bCanZoomIn
= bCanZoomIn
;
3477 g_object_notify_by_pspec(G_OBJECT(pDocView
), properties
[PROP_CAN_ZOOM_IN
]);
3479 if (bCanZoomOut
!= bool(priv
->m_bCanZoomOut
))
3481 priv
->m_bCanZoomOut
= bCanZoomOut
;
3482 g_object_notify_by_pspec(G_OBJECT(pDocView
), properties
[PROP_CAN_ZOOM_OUT
]);
3485 // Update the client's view size
3486 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
3487 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_CLIENT_ZOOM
);
3488 pLOEvent
->m_nTilePixelWidth
= nTileSizePixels
;
3489 pLOEvent
->m_nTilePixelHeight
= nTileSizePixels
;
3490 pLOEvent
->m_nTileTwipWidth
= pixelToTwip(nTileSizePixels
, fZoom
);
3491 pLOEvent
->m_nTileTwipHeight
= pixelToTwip(nTileSizePixels
, fZoom
);
3492 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
3494 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
3495 if (error
!= nullptr)
3497 g_warning("Unable to call LOK_SET_CLIENT_ZOOM: %s", error
->message
);
3498 g_clear_error(&error
);
3500 g_object_unref(task
);
3502 priv
->m_nTileSizeTwips
= pixelToTwip(nTileSizePixels
, priv
->m_fZoom
);
3505 SAL_DLLPUBLIC_EXPORT gfloat
3506 lok_doc_view_get_zoom (LOKDocView
* pDocView
)
3508 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3509 return priv
->m_fZoom
;
3512 SAL_DLLPUBLIC_EXPORT gint
3513 lok_doc_view_get_parts (LOKDocView
* pDocView
)
3515 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3516 if (!priv
->m_pDocument
)
3519 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
3520 std::stringstream ss
;
3521 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
3522 g_info("%s", ss
.str().c_str());
3523 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
3524 return priv
->m_pDocument
->pClass
->getParts( priv
->m_pDocument
);
3527 SAL_DLLPUBLIC_EXPORT gint
3528 lok_doc_view_get_part (LOKDocView
* pDocView
)
3530 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3531 if (!priv
->m_pDocument
)
3534 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
3535 std::stringstream ss
;
3536 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
3537 g_info("%s", ss
.str().c_str());
3538 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
3539 return priv
->m_pDocument
->pClass
->getPart( priv
->m_pDocument
);
3542 SAL_DLLPUBLIC_EXPORT
void
3543 lok_doc_view_set_part (LOKDocView
* pDocView
, int nPart
)
3545 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3546 if (!priv
->m_pDocument
)
3549 if (nPart
< 0 || nPart
>= priv
->m_nParts
)
3551 g_warning("Invalid part request : %d", nPart
);
3555 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
3556 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_PART
);
3557 GError
* error
= nullptr;
3559 pLOEvent
->m_nPart
= nPart
;
3560 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
3562 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
3563 if (error
!= nullptr)
3565 g_warning("Unable to call LOK_SET_PART: %s", error
->message
);
3566 g_clear_error(&error
);
3568 g_object_unref(task
);
3569 priv
->m_nPartId
= nPart
;
3572 SAL_DLLPUBLIC_EXPORT gchar
*
3573 lok_doc_view_get_part_name (LOKDocView
* pDocView
, int nPart
)
3575 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3576 if (!priv
->m_pDocument
)
3579 std::scoped_lock
<std::mutex
> aGuard(g_aLOKMutex
);
3580 std::stringstream ss
;
3581 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
3582 g_info("%s", ss
.str().c_str());
3583 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
3584 return priv
->m_pDocument
->pClass
->getPartName( priv
->m_pDocument
, nPart
);
3587 SAL_DLLPUBLIC_EXPORT
void
3588 lok_doc_view_set_partmode(LOKDocView
* pDocView
,
3591 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3592 if (!priv
->m_pDocument
)
3595 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
3596 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_PARTMODE
);
3597 GError
* error
= nullptr;
3599 pLOEvent
->m_nPartMode
= nPartMode
;
3600 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
3602 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
3603 if (error
!= nullptr)
3605 g_warning("Unable to call LOK_SET_PARTMODE: %s", error
->message
);
3606 g_clear_error(&error
);
3608 g_object_unref(task
);
3611 SAL_DLLPUBLIC_EXPORT
void
3612 lok_doc_view_reset_view(LOKDocView
* pDocView
)
3614 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3616 if (priv
->m_pTileBuffer
!= nullptr)
3617 priv
->m_pTileBuffer
->resetAllTiles();
3618 priv
->m_nLoadProgress
= 0.0;
3620 memset(&priv
->m_aVisibleCursor
, 0, sizeof(priv
->m_aVisibleCursor
));
3621 priv
->m_bCursorOverlayVisible
= false;
3622 priv
->m_bCursorVisible
= false;
3624 priv
->m_nLastButtonPressTime
= 0;
3625 priv
->m_nLastButtonReleaseTime
= 0;
3626 priv
->m_aTextSelectionRectangles
.clear();
3628 memset(&priv
->m_aTextSelectionStart
, 0, sizeof(priv
->m_aTextSelectionStart
));
3629 memset(&priv
->m_aTextSelectionEnd
, 0, sizeof(priv
->m_aTextSelectionEnd
));
3630 memset(&priv
->m_aGraphicSelection
, 0, sizeof(priv
->m_aGraphicSelection
));
3631 priv
->m_bInDragGraphicSelection
= false;
3632 memset(&priv
->m_aCellCursor
, 0, sizeof(priv
->m_aCellCursor
));
3634 cairo_surface_destroy(priv
->m_pHandleStart
);
3635 priv
->m_pHandleStart
= nullptr;
3636 memset(&priv
->m_aHandleStartRect
, 0, sizeof(priv
->m_aHandleStartRect
));
3637 priv
->m_bInDragStartHandle
= false;
3639 cairo_surface_destroy(priv
->m_pHandleMiddle
);
3640 priv
->m_pHandleMiddle
= nullptr;
3641 memset(&priv
->m_aHandleMiddleRect
, 0, sizeof(priv
->m_aHandleMiddleRect
));
3642 priv
->m_bInDragMiddleHandle
= false;
3644 cairo_surface_destroy(priv
->m_pHandleEnd
);
3645 priv
->m_pHandleEnd
= nullptr;
3646 memset(&priv
->m_aHandleEndRect
, 0, sizeof(priv
->m_aHandleEndRect
));
3647 priv
->m_bInDragEndHandle
= false;
3649 memset(&priv
->m_aGraphicHandleRects
, 0, sizeof(priv
->m_aGraphicHandleRects
));
3650 memset(&priv
->m_bInDragGraphicHandles
, 0, sizeof(priv
->m_bInDragGraphicHandles
));
3652 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
3655 SAL_DLLPUBLIC_EXPORT
void
3656 lok_doc_view_set_edit(LOKDocView
* pDocView
,
3659 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3660 if (!priv
->m_pDocument
)
3663 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
3664 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_EDIT
);
3665 GError
* error
= nullptr;
3667 pLOEvent
->m_bEdit
= bEdit
;
3668 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
3670 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
3671 if (error
!= nullptr)
3673 g_warning("Unable to call LOK_SET_EDIT: %s", error
->message
);
3674 g_clear_error(&error
);
3676 g_object_unref(task
);
3679 SAL_DLLPUBLIC_EXPORT gboolean
3680 lok_doc_view_get_edit (LOKDocView
* pDocView
)
3682 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3683 return priv
->m_bEdit
;
3686 SAL_DLLPUBLIC_EXPORT
void
3687 lok_doc_view_post_command (LOKDocView
* pDocView
,
3688 const gchar
* pCommand
,
3689 const gchar
* pArguments
,
3690 gboolean bNotifyWhenFinished
)
3692 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3693 if (!priv
->m_pDocument
)
3697 LOKPostCommand(pDocView
, pCommand
, pArguments
, bNotifyWhenFinished
);
3699 g_info ("LOK_POST_COMMAND: ignoring commands in view-only mode");
3702 SAL_DLLPUBLIC_EXPORT
void
3703 lok_doc_view_find_prev (LOKDocView
* pDocView
,
3705 gboolean bHighlightAll
)
3707 doSearch(pDocView
, pText
, true, bHighlightAll
);
3710 SAL_DLLPUBLIC_EXPORT
void
3711 lok_doc_view_find_next (LOKDocView
* pDocView
,
3713 gboolean bHighlightAll
)
3715 doSearch(pDocView
, pText
, false, bHighlightAll
);
3718 SAL_DLLPUBLIC_EXPORT
void
3719 lok_doc_view_highlight_all (LOKDocView
* pDocView
,
3722 doSearch(pDocView
, pText
, false, true);
3725 SAL_DLLPUBLIC_EXPORT gchar
*
3726 lok_doc_view_copy_selection (LOKDocView
* pDocView
,
3727 const gchar
* pMimeType
,
3728 gchar
** pUsedMimeType
)
3730 LibreOfficeKitDocument
* pDocument
= lok_doc_view_get_document(pDocView
);
3734 std::stringstream ss
;
3735 ss
<< "lok::Document::getTextSelection('" << pMimeType
<< "')";
3736 g_info("%s", ss
.str().c_str());
3737 return pDocument
->pClass
->getTextSelection(pDocument
, pMimeType
, pUsedMimeType
);
3740 SAL_DLLPUBLIC_EXPORT gboolean
3741 lok_doc_view_paste (LOKDocView
* pDocView
,
3742 const gchar
* pMimeType
,
3746 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3747 LibreOfficeKitDocument
* pDocument
= priv
->m_pDocument
;
3755 g_info ("ignoring paste in view-only mode");
3761 std::stringstream ss
;
3762 ss
<< "lok::Document::paste('" << pMimeType
<< "', '" << std::string(pData
, nSize
) << ", "<<nSize
<<"')";
3763 g_info("%s", ss
.str().c_str());
3764 ret
= pDocument
->pClass
->paste(pDocument
, pMimeType
, pData
, nSize
);
3770 SAL_DLLPUBLIC_EXPORT
void
3771 lok_doc_view_set_document_password (LOKDocView
* pDocView
,
3773 const gchar
* pPassword
)
3775 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3777 priv
->m_pOffice
->pClass
->setDocumentPassword(priv
->m_pOffice
, pURL
, pPassword
);
3780 SAL_DLLPUBLIC_EXPORT gchar
*
3781 lok_doc_view_get_version_info (LOKDocView
* pDocView
)
3783 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3785 return priv
->m_pOffice
->pClass
->getVersionInfo(priv
->m_pOffice
);
3789 SAL_DLLPUBLIC_EXPORT gfloat
3790 lok_doc_view_pixel_to_twip (LOKDocView
* pDocView
, float fInput
)
3792 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3793 return pixelToTwip(fInput
, priv
->m_fZoom
);
3796 SAL_DLLPUBLIC_EXPORT gfloat
3797 lok_doc_view_twip_to_pixel (LOKDocView
* pDocView
, float fInput
)
3799 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3800 return twipToPixel(fInput
, priv
->m_fZoom
);
3803 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */