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 <rsc/rsc-vcl-shared-types.hxx>
27 #include <vcl/event.hxx>
29 #include "tilebuffer.hxx"
31 #if !GLIB_CHECK_VERSION(2,32,0)
32 #define G_SOURCE_REMOVE FALSE
33 #define G_SOURCE_CONTINUE TRUE
35 #if !GLIB_CHECK_VERSION(2,40,0)
36 #define g_info(...) g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, __VA_ARGS__)
39 // Cursor bitmaps from the installation set.
40 #define CURSOR_HANDLE_DIR "/../share/libreofficekit/"
41 // Number of handles around a graphic selection.
42 #define GRAPHIC_HANDLE_COUNT 8
43 // Maximum Zoom allowed
45 // Minimum Zoom allowed
46 #define MIN_ZOOM 0.25f
48 /// This is expected to be locked during setView(), doSomethingElse() LOK calls.
49 static std::mutex g_aLOKMutex
;
51 /// Same as a GdkRectangle, but also tracks in which part the rectangle is.
55 GdkRectangle m_aRectangle
;
57 ViewRectangle(int nPart
= 0, const GdkRectangle
& rRectangle
= GdkRectangle())
59 m_aRectangle(rRectangle
)
64 /// Same as a list of GdkRectangles, but also tracks in which part the rectangle is.
68 std::vector
<GdkRectangle
> m_aRectangles
;
70 ViewRectangles(int nPart
= 0, const std::vector
<GdkRectangle
>& rRectangles
= std::vector
<GdkRectangle
>())
72 m_aRectangles(rRectangles
)
77 /// Private struct used by this GObject type
78 struct LOKDocViewPrivateImpl
80 const gchar
* m_aLOPath
;
81 const gchar
* m_pUserProfileURL
;
82 const gchar
* m_aDocPath
;
83 std::string m_aRenderingArguments
;
84 gdouble m_nLoadProgress
;
85 gboolean m_bIsLoading
;
86 gboolean m_bCanZoomIn
;
87 gboolean m_bCanZoomOut
;
88 LibreOfficeKit
* m_pOffice
;
89 LibreOfficeKitDocument
* m_pDocument
;
91 std::unique_ptr
<TileBuffer
> m_pTileBuffer
;
92 GThreadPool
* lokThreadPool
;
95 glong m_nDocumentWidthTwips
;
96 glong m_nDocumentHeightTwips
;
97 /// View or edit mode.
100 guint64 m_nLOKFeatures
;
101 /// Number of parts in currently loaded document
103 /// Position and size of the visible cursor.
104 GdkRectangle m_aVisibleCursor
;
105 /// Position and size of the view cursors. The current view can only see
106 /// them, can't modify them. Key is the view id.
107 std::map
<int, ViewRectangle
> m_aViewCursors
;
108 /// Cursor overlay is visible or hidden (for blinking).
109 gboolean m_bCursorOverlayVisible
;
110 /// Cursor is visible or hidden (e.g. for graphic selection).
111 gboolean m_bCursorVisible
;
112 /// Visibility of view selections. The current view can only see / them,
113 /// can't modify them. Key is the view id.
114 std::map
<int, bool> m_aViewCursorVisibilities
;
115 /// Time of the last button press.
116 guint32 m_nLastButtonPressTime
;
117 /// Time of the last button release.
118 guint32 m_nLastButtonReleaseTime
;
119 /// Last pressed button (left, right, middle)
120 guint32 m_nLastButtonPressed
;
121 /// Key modifier (ctrl, atl, shift)
122 guint32 m_nKeyModifier
;
123 /// Rectangles of the current text selection.
124 std::vector
<GdkRectangle
> m_aTextSelectionRectangles
;
125 /// Rectangles of view selections. The current view can only see
126 /// them, can't modify them. Key is the view id.
127 std::map
<int, ViewRectangles
> m_aTextViewSelectionRectangles
;
128 /// Position and size of the selection start (as if there would be a cursor caret there).
129 GdkRectangle m_aTextSelectionStart
;
130 /// Position and size of the selection end.
131 GdkRectangle m_aTextSelectionEnd
;
132 GdkRectangle m_aGraphicSelection
;
133 /// Position and size of the graphic view selections. The current view can only
134 /// see them, can't modify them. Key is the view id.
135 std::map
<int, ViewRectangle
> m_aGraphicViewSelections
;
136 GdkRectangle m_aCellCursor
;
137 /// Position and size of the cell view cursors. The current view can only
138 /// see them, can't modify them. Key is the view id.
139 std::map
<int, ViewRectangle
> m_aCellViewCursors
;
140 gboolean m_bInDragGraphicSelection
;
142 /// @name Start/middle/end handle.
144 /// Bitmap of the text selection start handle.
145 cairo_surface_t
* m_pHandleStart
;
146 /// Rectangle of the text selection start handle, to know if the user clicked on it or not
147 GdkRectangle m_aHandleStartRect
;
148 /// If we are in the middle of a drag of the text selection end handle.
149 gboolean m_bInDragStartHandle
;
150 /// Bitmap of the text selection middle handle.
151 cairo_surface_t
* m_pHandleMiddle
;
152 /// Rectangle of the text selection middle handle, to know if the user clicked on it or not
153 GdkRectangle m_aHandleMiddleRect
;
154 /// If we are in the middle of a drag of the text selection middle handle.
155 gboolean m_bInDragMiddleHandle
;
156 /// Bitmap of the text selection end handle.
157 cairo_surface_t
* m_pHandleEnd
;
158 /// Rectangle of the text selection end handle, to know if the user clicked on it or not
159 GdkRectangle m_aHandleEndRect
;
160 /// If we are in the middle of a drag of the text selection end handle.
161 gboolean m_bInDragEndHandle
;
164 /// @name Graphic handles.
166 /// Rectangle of a graphic selection handle, to know if the user clicked on it or not.
167 GdkRectangle m_aGraphicHandleRects
[8];
168 /// If we are in the middle of a drag of a graphic selection handle.
169 gboolean m_bInDragGraphicHandles
[8];
172 /// View ID, returned by createView() or 0 by default.
175 /// Cached part ID, returned by getPart().
178 /// Cached document type, returned by getDocumentType().
179 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.
185 int m_nTileSizeTwips
;
187 GdkRectangle m_aVisibleArea
;
188 bool m_bVisibleAreaSet
;
190 /// Event source ID for handleTimeout() of this widget.
193 /// Rectangles of view locks. The current view can only see
194 /// them, can't modify them. Key is the view id.
195 std::map
<int, ViewRectangle
> m_aViewLockRectangles
;
197 LOKDocViewPrivateImpl()
198 : m_aLOPath(nullptr),
199 m_pUserProfileURL(nullptr),
206 m_pDocument(nullptr),
207 lokThreadPool(nullptr),
209 m_nDocumentWidthTwips(0),
210 m_nDocumentHeightTwips(0),
214 m_aVisibleCursor({0, 0, 0, 0}),
215 m_bCursorOverlayVisible(false),
216 m_bCursorVisible(true),
217 m_nLastButtonPressTime(0),
218 m_nLastButtonReleaseTime(0),
219 m_nLastButtonPressed(0),
221 m_aTextSelectionStart({0, 0, 0, 0}),
222 m_aTextSelectionEnd({0, 0, 0, 0}),
223 m_aGraphicSelection({0, 0, 0, 0}),
224 m_aCellCursor({0, 0, 0, 0}),
225 m_bInDragGraphicSelection(false),
226 m_pHandleStart(nullptr),
227 m_aHandleStartRect({0, 0, 0, 0}),
228 m_bInDragStartHandle(0),
229 m_pHandleMiddle(nullptr),
230 m_aHandleMiddleRect({0, 0, 0, 0}),
231 m_bInDragMiddleHandle(false),
232 m_pHandleEnd(nullptr),
233 m_aHandleEndRect({0, 0, 0, 0}),
234 m_bInDragEndHandle(false),
237 m_eDocumentType(LOK_DOCTYPE_OTHER
),
239 m_aVisibleArea({0, 0, 0, 0}),
240 m_bVisibleAreaSet(false),
243 memset(&m_aGraphicHandleRects
, 0, sizeof(m_aGraphicHandleRects
));
244 memset(&m_bInDragGraphicHandles
, 0, sizeof(m_bInDragGraphicHandles
));
247 ~LOKDocViewPrivateImpl()
249 g_source_remove(m_nTimeoutId
);
253 /// Wrapper around LOKDocViewPrivateImpl, managed by malloc/memset/free.
254 struct _LOKDocViewPrivate
256 LOKDocViewPrivateImpl
* m_pImpl
;
258 LOKDocViewPrivateImpl
* operator->()
291 PROP_USER_PROFILE_URL
,
303 PROP_DOC_PASSWORD_TO_MODIFY
,
304 PROP_TILED_ANNOTATIONS
,
309 static guint doc_view_signals
[LAST_SIGNAL
] = { 0 };
310 static GParamSpec
*properties
[PROP_LAST
] = { nullptr };
312 static void lok_doc_view_initable_iface_init (GInitableIface
*iface
);
313 static void callbackWorker (int nType
, const char* pPayload
, void* pData
);
315 SAL_DLLPUBLIC_EXPORT GType
lok_doc_view_get_type();
317 #pragma GCC diagnostic push
318 #pragma GCC diagnostic ignored "-Wunused-function"
320 G_DEFINE_TYPE_WITH_CODE (LOKDocView
, lok_doc_view
, GTK_TYPE_DRAWING_AREA
,
321 G_ADD_PRIVATE (LOKDocView
)
322 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE
, lok_doc_view_initable_iface_init
));
324 #pragma GCC diagnostic pop
327 static LOKDocViewPrivate
& getPrivate(LOKDocView
* pDocView
)
329 LOKDocViewPrivate
* priv
= static_cast<LOKDocViewPrivate
*>(lok_doc_view_get_instance_private(pDocView
));
333 /// Helper struct used to pass the data from soffice thread -> main thread.
337 std::string m_aPayload
;
338 LOKDocView
* m_pDocView
;
340 CallbackData(int nType
, const std::string
& rPayload
, LOKDocView
* pDocView
)
342 m_aPayload(rPayload
),
343 m_pDocView(pDocView
) {}
347 payloadToSize(const char* pPayload
, long& rWidth
, long& rHeight
)
349 rWidth
= rHeight
= 0;
350 gchar
** ppCoordinates
= g_strsplit(pPayload
, ", ", 2);
351 gchar
** ppCoordinate
= ppCoordinates
;
354 rWidth
= atoi(*ppCoordinate
);
358 rHeight
= atoi(*ppCoordinate
);
359 g_strfreev(ppCoordinates
);
362 /// Returns the string representation of a LibreOfficeKitCallbackType enumeration element.
364 callbackTypeToString (int nType
)
368 case LOK_CALLBACK_INVALIDATE_TILES
:
369 return "LOK_CALLBACK_INVALIDATE_TILES";
370 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
:
371 return "LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR";
372 case LOK_CALLBACK_TEXT_SELECTION
:
373 return "LOK_CALLBACK_TEXT_SELECTION";
374 case LOK_CALLBACK_TEXT_SELECTION_START
:
375 return "LOK_CALLBACK_TEXT_SELECTION_START";
376 case LOK_CALLBACK_TEXT_SELECTION_END
:
377 return "LOK_CALLBACK_TEXT_SELECTION_END";
378 case LOK_CALLBACK_CURSOR_VISIBLE
:
379 return "LOK_CALLBACK_CURSOR_VISIBLE";
380 case LOK_CALLBACK_VIEW_CURSOR_VISIBLE
:
381 return "LOK_CALLBACK_VIEW_CURSOR_VISIBLE";
382 case LOK_CALLBACK_GRAPHIC_SELECTION
:
383 return "LOK_CALLBACK_GRAPHIC_SELECTION";
384 case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION
:
385 return "LOK_CALLBACK_GRAPHIC_VIEW_SELECTION";
386 case LOK_CALLBACK_CELL_CURSOR
:
387 return "LOK_CALLBACK_CELL_CURSOR";
388 case LOK_CALLBACK_HYPERLINK_CLICKED
:
389 return "LOK_CALLBACK_HYPERLINK_CLICKED";
390 case LOK_CALLBACK_MOUSE_POINTER
:
391 return "LOK_CALLBACK_MOUSE_POINTER";
392 case LOK_CALLBACK_STATE_CHANGED
:
393 return "LOK_CALLBACK_STATE_CHANGED";
394 case LOK_CALLBACK_STATUS_INDICATOR_START
:
395 return "LOK_CALLBACK_STATUS_INDICATOR_START";
396 case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE
:
397 return "LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE";
398 case LOK_CALLBACK_STATUS_INDICATOR_FINISH
:
399 return "LOK_CALLBACK_STATUS_INDICATOR_FINISH";
400 case LOK_CALLBACK_SEARCH_NOT_FOUND
:
401 return "LOK_CALLBACK_SEARCH_NOT_FOUND";
402 case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED
:
403 return "LOK_CALLBACK_DOCUMENT_SIZE_CHANGED";
404 case LOK_CALLBACK_SET_PART
:
405 return "LOK_CALLBACK_SET_PART";
406 case LOK_CALLBACK_SEARCH_RESULT_SELECTION
:
407 return "LOK_CALLBACK_SEARCH_RESULT_SELECTION";
408 case LOK_CALLBACK_DOCUMENT_PASSWORD
:
409 return "LOK_CALLBACK_DOCUMENT_PASSWORD";
410 case LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY
:
411 return "LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY";
412 case LOK_CALLBACK_CONTEXT_MENU
:
413 return "LOK_CALLBACK_CONTEXT_MENU";
414 case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR
:
415 return "LOK_CALLBACK_INVALIDATE_VIEW_CURSOR";
416 case LOK_CALLBACK_TEXT_VIEW_SELECTION
:
417 return "LOK_CALLBACK_TEXT_VIEW_SELECTION";
418 case LOK_CALLBACK_CELL_VIEW_CURSOR
:
419 return "LOK_CALLBACK_CELL_VIEW_CURSOR";
420 case LOK_CALLBACK_CELL_ADDRESS
:
421 return "LOK_CALLBACK_CELL_ADDRESS";
422 case LOK_CALLBACK_CELL_FORMULA
:
423 return "LOK_CALLBACK_CELL_FORMULA";
424 case LOK_CALLBACK_UNO_COMMAND_RESULT
:
425 return "LOK_CALLBACK_UNO_COMMAND_RESULT";
426 case LOK_CALLBACK_ERROR
:
427 return "LOK_CALLBACK_ERROR";
428 case LOK_CALLBACK_VIEW_LOCK
:
429 return "LOK_CALLBACK_VIEW_LOCK";
430 case LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED
:
431 return "LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED";
432 case LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED
:
433 return "LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED";
434 case LOK_CALLBACK_COMMENT
:
435 return "LOK_CALLBACK_COMMENT";
442 LOKPostCommand (LOKDocView
* pDocView
,
443 const gchar
* pCommand
,
444 const gchar
* pArguments
,
445 gboolean bNotifyWhenFinished
)
447 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
448 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
449 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_COMMAND
);
450 GError
* error
= nullptr;
451 pLOEvent
->m_pCommand
= g_strdup(pCommand
);
452 pLOEvent
->m_pArguments
= g_strdup(pArguments
);
453 pLOEvent
->m_bNotifyWhenFinished
= bNotifyWhenFinished
;
455 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
456 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
457 if (error
!= nullptr)
459 g_warning("Unable to call LOK_POST_COMMAND: %s", error
->message
);
460 g_clear_error(&error
);
462 g_object_unref(task
);
466 doSearch(LOKDocView
* pDocView
, const char* pText
, bool bBackwards
, bool highlightAll
)
468 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
469 if (!priv
->m_pDocument
)
472 boost::property_tree::ptree aTree
;
473 GtkWidget
* drawingWidget
= GTK_WIDGET(pDocView
);
474 GdkWindow
* drawingWindow
= gtk_widget_get_window(drawingWidget
);
477 std::shared_ptr
<cairo_region_t
> cairoVisRegion( gdk_window_get_visible_region(drawingWindow
),
478 cairo_region_destroy
);
479 cairo_rectangle_int_t cairoVisRect
;
480 cairo_region_get_rectangle(cairoVisRegion
.get(), 0, &cairoVisRect
);
481 int x
= pixelToTwip (cairoVisRect
.x
, priv
->m_fZoom
);
482 int y
= pixelToTwip (cairoVisRect
.y
, priv
->m_fZoom
);
484 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchString/type", '/'), "string");
485 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchString/value", '/'), pText
);
486 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.Backward/type", '/'), "boolean");
487 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.Backward/value", '/'), bBackwards
);
490 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.Command/type", '/'), "unsigned short");
491 // SvxSearchCmd::FIND_ALL
492 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.Command/value", '/'), "1");
495 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointX/type", '/'), "long");
496 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointX/value", '/'), x
);
497 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointY/type", '/'), "long");
498 aTree
.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointY/value", '/'), y
);
500 std::stringstream aStream
;
501 boost::property_tree::write_json(aStream
, aTree
);
503 LOKPostCommand (pDocView
, ".uno:ExecuteSearch", aStream
.str().c_str(), false);
507 isEmptyRectangle(const GdkRectangle
& rRectangle
)
509 return rRectangle
.x
== 0 && rRectangle
.y
== 0 && rRectangle
.width
== 0 && rRectangle
.height
== 0;
512 /// if handled, returns TRUE else FALSE
514 handleTextSelectionOnButtonPress(GdkRectangle
& aClick
, LOKDocView
* pDocView
) {
515 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
517 if (gdk_rectangle_intersect(&aClick
, &priv
->m_aHandleStartRect
, nullptr))
519 g_info("LOKDocView_Impl::signalButton: start of drag start handle");
520 priv
->m_bInDragStartHandle
= true;
523 else if (gdk_rectangle_intersect(&aClick
, &priv
->m_aHandleMiddleRect
, nullptr))
525 g_info("LOKDocView_Impl::signalButton: start of drag middle handle");
526 priv
->m_bInDragMiddleHandle
= true;
529 else if (gdk_rectangle_intersect(&aClick
, &priv
->m_aHandleEndRect
, nullptr))
531 g_info("LOKDocView_Impl::signalButton: start of drag end handle");
532 priv
->m_bInDragEndHandle
= true;
539 /// if handled, returns TRUE else FALSE
541 handleGraphicSelectionOnButtonPress(GdkRectangle
& aClick
, LOKDocView
* pDocView
) {
542 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
543 GError
* error
= nullptr;
545 for (int i
= 0; i
< GRAPHIC_HANDLE_COUNT
; ++i
)
547 if (gdk_rectangle_intersect(&aClick
, &priv
->m_aGraphicHandleRects
[i
], nullptr))
549 g_info("LOKDocView_Impl::signalButton: start of drag graphic handle #%d", i
);
550 priv
->m_bInDragGraphicHandles
[i
] = true;
552 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
553 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_GRAPHIC_SELECTION
);
554 pLOEvent
->m_nSetGraphicSelectionType
= LOK_SETGRAPHICSELECTION_START
;
555 pLOEvent
->m_nSetGraphicSelectionX
= pixelToTwip(priv
->m_aGraphicHandleRects
[i
].x
+ priv
->m_aGraphicHandleRects
[i
].width
/ 2, priv
->m_fZoom
);
556 pLOEvent
->m_nSetGraphicSelectionY
= pixelToTwip(priv
->m_aGraphicHandleRects
[i
].y
+ priv
->m_aGraphicHandleRects
[i
].height
/ 2, priv
->m_fZoom
);
557 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
559 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
560 if (error
!= nullptr)
562 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error
->message
);
563 g_clear_error(&error
);
565 g_object_unref(task
);
574 /// if handled, returns TRUE else FALSE
576 handleTextSelectionOnButtonRelease(LOKDocView
* pDocView
) {
577 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
579 if (priv
->m_bInDragStartHandle
)
581 g_info("LOKDocView_Impl::signalButton: end of drag start handle");
582 priv
->m_bInDragStartHandle
= false;
585 else if (priv
->m_bInDragMiddleHandle
)
587 g_info("LOKDocView_Impl::signalButton: end of drag middle handle");
588 priv
->m_bInDragMiddleHandle
= false;
591 else if (priv
->m_bInDragEndHandle
)
593 g_info("LOKDocView_Impl::signalButton: end of drag end handle");
594 priv
->m_bInDragEndHandle
= false;
601 /// if handled, returns TRUE else FALSE
603 handleGraphicSelectionOnButtonRelease(LOKDocView
* pDocView
, GdkEventButton
* pEvent
) {
604 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
605 GError
* error
= nullptr;
607 for (int i
= 0; i
< GRAPHIC_HANDLE_COUNT
; ++i
)
609 if (priv
->m_bInDragGraphicHandles
[i
])
611 g_info("LOKDocView_Impl::signalButton: end of drag graphic handle #%d", i
);
612 priv
->m_bInDragGraphicHandles
[i
] = false;
614 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
615 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_GRAPHIC_SELECTION
);
616 pLOEvent
->m_nSetGraphicSelectionType
= LOK_SETGRAPHICSELECTION_END
;
617 pLOEvent
->m_nSetGraphicSelectionX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
618 pLOEvent
->m_nSetGraphicSelectionY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
619 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
621 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
622 if (error
!= nullptr)
624 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error
->message
);
625 g_clear_error(&error
);
627 g_object_unref(task
);
633 if (priv
->m_bInDragGraphicSelection
)
635 g_info("LOKDocView_Impl::signalButton: end of drag graphic selection");
636 priv
->m_bInDragGraphicSelection
= false;
638 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
639 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_GRAPHIC_SELECTION
);
640 pLOEvent
->m_nSetGraphicSelectionType
= LOK_SETGRAPHICSELECTION_END
;
641 pLOEvent
->m_nSetGraphicSelectionX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
642 pLOEvent
->m_nSetGraphicSelectionY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
643 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
645 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
646 if (error
!= nullptr)
648 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error
->message
);
649 g_clear_error(&error
);
651 g_object_unref(task
);
660 postKeyEventInThread(gpointer data
)
662 GTask
* task
= G_TASK(data
);
663 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
664 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
665 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
667 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
668 std::stringstream ss
;
669 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
670 g_info("%s", ss
.str().c_str());
671 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
673 if (priv
->m_nTileSizeTwips
)
675 ss
.str(std::string());
676 ss
<< "lok::Document::setClientZoom(" << nTileSizePixels
<< ", " << nTileSizePixels
<< ", " << priv
->m_nTileSizeTwips
<< ", " << priv
->m_nTileSizeTwips
<< ")";
677 g_info("%s", ss
.str().c_str());
678 priv
->m_pDocument
->pClass
->setClientZoom(priv
->m_pDocument
,
681 priv
->m_nTileSizeTwips
,
682 priv
->m_nTileSizeTwips
);
683 priv
->m_nTileSizeTwips
= 0;
685 if (priv
->m_bVisibleAreaSet
)
687 ss
.str(std::string());
688 ss
<< "lok::Document::setClientVisibleArea(" << priv
->m_aVisibleArea
.x
<< ", " << priv
->m_aVisibleArea
.y
<< ", ";
689 ss
<< priv
->m_aVisibleArea
.width
<< ", " << priv
->m_aVisibleArea
.height
<< ")";
690 g_info("%s", ss
.str().c_str());
691 priv
->m_pDocument
->pClass
->setClientVisibleArea(priv
->m_pDocument
,
692 priv
->m_aVisibleArea
.x
,
693 priv
->m_aVisibleArea
.y
,
694 priv
->m_aVisibleArea
.width
,
695 priv
->m_aVisibleArea
.height
);
696 priv
->m_bVisibleAreaSet
= false;
699 ss
.str(std::string());
700 ss
<< "lok::Document::postKeyEvent(" << pLOEvent
->m_nKeyEvent
<< ", " << pLOEvent
->m_nCharCode
<< ", " << pLOEvent
->m_nKeyCode
<< ")";
701 g_info("%s", ss
.str().c_str());
702 priv
->m_pDocument
->pClass
->postKeyEvent(priv
->m_pDocument
,
703 pLOEvent
->m_nKeyEvent
,
704 pLOEvent
->m_nCharCode
,
705 pLOEvent
->m_nKeyCode
);
709 signalKey (GtkWidget
* pWidget
, GdkEventKey
* pEvent
)
711 LOKDocView
* pDocView
= LOK_DOC_VIEW(pWidget
);
712 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
715 GError
* error
= nullptr;
719 g_info("signalKey: not in edit mode, ignore");
723 priv
->m_nKeyModifier
&= KEY_MOD2
;
724 switch (pEvent
->keyval
)
726 case GDK_KEY_BackSpace
:
727 nKeyCode
= com::sun::star::awt::Key::BACKSPACE
;
730 nKeyCode
= com::sun::star::awt::Key::DELETE
;
733 case GDK_KEY_KP_Enter
:
734 nKeyCode
= com::sun::star::awt::Key::RETURN
;
737 nKeyCode
= com::sun::star::awt::Key::ESCAPE
;
740 nKeyCode
= com::sun::star::awt::Key::TAB
;
743 nKeyCode
= com::sun::star::awt::Key::DOWN
;
746 nKeyCode
= com::sun::star::awt::Key::UP
;
749 nKeyCode
= com::sun::star::awt::Key::LEFT
;
752 nKeyCode
= com::sun::star::awt::Key::RIGHT
;
754 case GDK_KEY_Page_Down
:
755 nKeyCode
= com::sun::star::awt::Key::PAGEDOWN
;
757 case GDK_KEY_Page_Up
:
758 nKeyCode
= com::sun::star::awt::Key::PAGEUP
;
761 nKeyCode
= com::sun::star::awt::Key::INSERT
;
763 case GDK_KEY_Shift_L
:
764 case GDK_KEY_Shift_R
:
765 if (pEvent
->type
== GDK_KEY_PRESS
)
766 priv
->m_nKeyModifier
|= KEY_SHIFT
;
768 case GDK_KEY_Control_L
:
769 case GDK_KEY_Control_R
:
770 if (pEvent
->type
== GDK_KEY_PRESS
)
771 priv
->m_nKeyModifier
|= KEY_MOD1
;
775 if (pEvent
->type
== GDK_KEY_PRESS
)
776 priv
->m_nKeyModifier
|= KEY_MOD2
;
778 priv
->m_nKeyModifier
&= ~KEY_MOD2
;
781 if (pEvent
->keyval
>= GDK_KEY_F1
&& pEvent
->keyval
<= GDK_KEY_F26
)
782 nKeyCode
= com::sun::star::awt::Key::F1
+ (pEvent
->keyval
- GDK_KEY_F1
);
784 nCharCode
= gdk_keyval_to_unicode(pEvent
->keyval
);
787 // rsc is not public API, but should be good enough for debugging purposes.
788 // If this is needed for real, then probably a new param of type
789 // css::awt::KeyModifier is needed in postKeyEvent().
790 if (pEvent
->state
& GDK_SHIFT_MASK
)
791 nKeyCode
|= KEY_SHIFT
;
793 if (pEvent
->state
& GDK_CONTROL_MASK
)
794 nKeyCode
|= KEY_MOD1
;
796 if (priv
->m_nKeyModifier
& KEY_MOD2
)
797 nKeyCode
|= KEY_MOD2
;
799 if (nKeyCode
& (KEY_SHIFT
| KEY_MOD1
| KEY_MOD2
)) {
800 if (pEvent
->keyval
>= GDK_KEY_a
&& pEvent
->keyval
<= GDK_KEY_z
)
802 nKeyCode
|= 512 + (pEvent
->keyval
- GDK_KEY_a
);
804 else if (pEvent
->keyval
>= GDK_KEY_A
&& pEvent
->keyval
<= GDK_KEY_Z
) {
805 nKeyCode
|= 512 + (pEvent
->keyval
- GDK_KEY_A
);
807 else if (pEvent
->keyval
>= GDK_KEY_0
&& pEvent
->keyval
<= GDK_KEY_9
) {
808 nKeyCode
|= 256 + (pEvent
->keyval
- GDK_KEY_0
);
812 if (pEvent
->type
== GDK_KEY_RELEASE
)
814 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
815 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_KEY
);
816 pLOEvent
->m_nKeyEvent
= LOK_KEYEVENT_KEYUP
;
817 pLOEvent
->m_nCharCode
= nCharCode
;
818 pLOEvent
->m_nKeyCode
= nKeyCode
;
819 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
820 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
821 if (error
!= nullptr)
823 g_warning("Unable to call LOK_POST_KEY: %s", error
->message
);
824 g_clear_error(&error
);
826 g_object_unref(task
);
830 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
831 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_KEY
);
832 pLOEvent
->m_nKeyEvent
= LOK_KEYEVENT_KEYINPUT
;
833 pLOEvent
->m_nCharCode
= nCharCode
;
834 pLOEvent
->m_nKeyCode
= nKeyCode
;
835 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
836 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
837 if (error
!= nullptr)
839 g_warning("Unable to call LOK_POST_KEY: %s", error
->message
);
840 g_clear_error(&error
);
842 g_object_unref(task
);
849 handleTimeout (gpointer pData
)
851 LOKDocView
* pDocView
= LOK_DOC_VIEW (pData
);
852 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
856 if (priv
->m_bCursorOverlayVisible
)
857 priv
->m_bCursorOverlayVisible
= false;
859 priv
->m_bCursorOverlayVisible
= true;
860 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
863 return G_SOURCE_CONTINUE
;
867 commandChanged(LOKDocView
* pDocView
, const std::string
& rString
)
869 g_signal_emit(pDocView
, doc_view_signals
[COMMAND_CHANGED
], 0, rString
.c_str());
873 searchNotFound(LOKDocView
* pDocView
, const std::string
& rString
)
875 g_signal_emit(pDocView
, doc_view_signals
[SEARCH_NOT_FOUND
], 0, rString
.c_str());
878 static void searchResultCount(LOKDocView
* pDocView
, const std::string
& rString
)
880 g_signal_emit(pDocView
, doc_view_signals
[SEARCH_RESULT_COUNT
], 0, rString
.c_str());
883 static void commandResult(LOKDocView
* pDocView
, const std::string
& rString
)
885 g_signal_emit(pDocView
, doc_view_signals
[COMMAND_RESULT
], 0, rString
.c_str());
888 static void addressChanged(LOKDocView
* pDocView
, const std::string
& rString
)
890 g_signal_emit(pDocView
, doc_view_signals
[ADDRESS_CHANGED
], 0, rString
.c_str());
893 static void formulaChanged(LOKDocView
* pDocView
, const std::string
& rString
)
895 g_signal_emit(pDocView
, doc_view_signals
[FORMULA_CHANGED
], 0, rString
.c_str());
898 static void reportError(LOKDocView
* /*pDocView*/, const std::string
& rString
)
900 GtkWidget
*dialog
= gtk_message_dialog_new(nullptr,
901 GTK_DIALOG_DESTROY_WITH_PARENT
,
906 gtk_dialog_run(GTK_DIALOG(dialog
));
907 gtk_widget_destroy(dialog
);
911 setPart(LOKDocView
* pDocView
, const std::string
& rString
)
913 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
914 priv
->m_nPartId
= std::stoi(rString
);
915 g_signal_emit(pDocView
, doc_view_signals
[PART_CHANGED
], 0, priv
->m_nPartId
);
919 hyperlinkClicked(LOKDocView
* pDocView
, const std::string
& rString
)
921 g_signal_emit(pDocView
, doc_view_signals
[HYPERLINK_CLICKED
], 0, rString
.c_str());
924 /// Trigger a redraw, invoked on the main thread by other functions running in a thread.
925 static gboolean
queueDraw(gpointer pData
)
927 GtkWidget
* pWidget
= static_cast<GtkWidget
*>(pData
);
929 gtk_widget_queue_draw(pWidget
);
931 return G_SOURCE_REMOVE
;
934 /// Looks up the author string from initializeForRendering()'s rendering arguments.
935 static std::string
getAuthorRenderingArgument(LOKDocViewPrivate
& priv
)
937 std::stringstream aStream
;
938 aStream
<< priv
->m_aRenderingArguments
;
939 boost::property_tree::ptree aTree
;
940 boost::property_tree::read_json(aStream
, aTree
);
942 for (const std::pair
<std::string
, boost::property_tree::ptree
>& rPair
: aTree
)
944 if (rPair
.first
== ".uno:Author")
946 aRet
= rPair
.second
.get
<std::string
>("value");
953 /// Author string <-> View ID map
954 static std::map
<std::string
, int> g_aAuthorViews
;
956 /// Set up LOKDocView after the document is loaded, invoked on the main thread by openDocumentInThread() running in a thread.
957 static gboolean
postDocumentLoad(gpointer pData
)
959 LOKDocView
* pLOKDocView
= static_cast<LOKDocView
*>(pData
);
960 LOKDocViewPrivate
& priv
= getPrivate(pLOKDocView
);
962 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
963 priv
->m_pDocument
->pClass
->initializeForRendering(priv
->m_pDocument
, priv
->m_aRenderingArguments
.c_str());
964 priv
->m_nViewId
= priv
->m_pDocument
->pClass
->getView(priv
->m_pDocument
);
965 g_aAuthorViews
[getAuthorRenderingArgument(priv
)] = priv
->m_nViewId
;
966 priv
->m_pDocument
->pClass
->registerCallback(priv
->m_pDocument
, callbackWorker
, pLOKDocView
);
967 priv
->m_pDocument
->pClass
->getDocumentSize(priv
->m_pDocument
, &priv
->m_nDocumentWidthTwips
, &priv
->m_nDocumentHeightTwips
);
968 priv
->m_nParts
= priv
->m_pDocument
->pClass
->getParts(priv
->m_pDocument
);
970 priv
->m_nTimeoutId
= g_timeout_add(600, handleTimeout
, pLOKDocView
);
972 float zoom
= priv
->m_fZoom
;
973 long nDocumentWidthTwips
= priv
->m_nDocumentWidthTwips
;
974 long nDocumentHeightTwips
= priv
->m_nDocumentHeightTwips
;
975 long nDocumentWidthPixels
= twipToPixel(nDocumentWidthTwips
, zoom
);
976 long nDocumentHeightPixels
= twipToPixel(nDocumentHeightTwips
, zoom
);
977 // Total number of columns in this document.
978 guint nColumns
= ceil((double)nDocumentWidthPixels
/ nTileSizePixels
);
980 priv
->m_pTileBuffer
= std::unique_ptr
<TileBuffer
>(new TileBuffer(nColumns
));
981 gtk_widget_set_size_request(GTK_WIDGET(pLOKDocView
),
982 nDocumentWidthPixels
,
983 nDocumentHeightPixels
);
984 gtk_widget_set_can_focus(GTK_WIDGET(pLOKDocView
), TRUE
);
985 gtk_widget_grab_focus(GTK_WIDGET(pLOKDocView
));
986 lok_doc_view_set_zoom(pLOKDocView
, 1.0);
988 return G_SOURCE_REMOVE
;
991 /// Implementation of the global callback handler, invoked by globalCallback();
993 globalCallback (gpointer pData
)
995 CallbackData
* pCallback
= static_cast<CallbackData
*>(pData
);
996 LOKDocViewPrivate
& priv
= getPrivate(pCallback
->m_pDocView
);
997 gboolean bModify
= false;
999 switch (pCallback
->m_nType
)
1001 case LOK_CALLBACK_STATUS_INDICATOR_START
:
1003 priv
->m_nLoadProgress
= 0.0;
1004 g_signal_emit (pCallback
->m_pDocView
, doc_view_signals
[LOAD_CHANGED
], 0, 0.0);
1007 case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE
:
1009 priv
->m_nLoadProgress
= static_cast<gdouble
>(std::stoi(pCallback
->m_aPayload
)/100.0);
1010 g_signal_emit (pCallback
->m_pDocView
, doc_view_signals
[LOAD_CHANGED
], 0, priv
->m_nLoadProgress
);
1013 case LOK_CALLBACK_STATUS_INDICATOR_FINISH
:
1015 priv
->m_nLoadProgress
= 1.0;
1016 g_signal_emit (pCallback
->m_pDocView
, doc_view_signals
[LOAD_CHANGED
], 0, 1.0);
1019 case LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY
:
1022 case LOK_CALLBACK_DOCUMENT_PASSWORD
:
1024 char const*const pURL(pCallback
->m_aPayload
.c_str());
1025 g_signal_emit (pCallback
->m_pDocView
, doc_view_signals
[PASSWORD_REQUIRED
], 0, pURL
, bModify
);
1028 case LOK_CALLBACK_ERROR
:
1030 reportError(pCallback
->m_pDocView
, pCallback
->m_aPayload
);
1039 return G_SOURCE_REMOVE
;
1043 globalCallbackWorker(int nType
, const char* pPayload
, void* pData
)
1045 LOKDocView
* pDocView
= LOK_DOC_VIEW (pData
);
1047 CallbackData
* pCallback
= new CallbackData(nType
, pPayload
? pPayload
: "(nil)", pDocView
);
1048 g_info("LOKDocView_Impl::globalCallbackWorkerImpl: %s, '%s'", callbackTypeToString(nType
), pPayload
);
1049 gdk_threads_add_idle(globalCallback
, pCallback
);
1053 payloadToRectangle (LOKDocView
* pDocView
, const char* pPayload
)
1055 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1057 // x, y, width, height, part number.
1058 gchar
** ppCoordinates
= g_strsplit(pPayload
, ", ", 5);
1059 gchar
** ppCoordinate
= ppCoordinates
;
1061 aRet
.width
= aRet
.height
= aRet
.x
= aRet
.y
= 0;
1065 aRet
.x
= atoi(*ppCoordinate
);
1071 aRet
.y
= atoi(*ppCoordinate
);
1077 long l
= atol(*ppCoordinate
);
1078 if (l
> std::numeric_limits
<int>::max())
1079 aRet
.width
= std::numeric_limits
<int>::max();
1082 if (aRet
.x
+ aRet
.width
> priv
->m_nDocumentWidthTwips
)
1083 aRet
.width
= priv
->m_nDocumentWidthTwips
- aRet
.x
;
1087 l
= atol(*ppCoordinate
);
1088 if (l
> std::numeric_limits
<int>::max())
1089 aRet
.height
= std::numeric_limits
<int>::max();
1092 if (aRet
.y
+ aRet
.height
> priv
->m_nDocumentHeightTwips
)
1093 aRet
.height
= priv
->m_nDocumentHeightTwips
- aRet
.y
;
1094 g_strfreev(ppCoordinates
);
1099 static const std::vector
<GdkRectangle
>
1100 payloadToRectangles(LOKDocView
* pDocView
, const char* pPayload
)
1102 std::vector
<GdkRectangle
> aRet
;
1104 if (g_strcmp0(pPayload
, "EMPTY") == 0)
1107 gchar
** ppRectangles
= g_strsplit(pPayload
, "; ", 0);
1108 for (gchar
** ppRectangle
= ppRectangles
; *ppRectangle
; ++ppRectangle
)
1109 aRet
.push_back(payloadToRectangle(pDocView
, *ppRectangle
));
1110 g_strfreev(ppRectangles
);
1117 setTilesInvalid (LOKDocView
* pDocView
, const GdkRectangle
& rRectangle
)
1119 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1120 GdkRectangle aRectanglePixels
;
1121 GdkPoint aStart
, aEnd
;
1123 aRectanglePixels
.x
= twipToPixel(rRectangle
.x
, priv
->m_fZoom
);
1124 aRectanglePixels
.y
= twipToPixel(rRectangle
.y
, priv
->m_fZoom
);
1125 aRectanglePixels
.width
= twipToPixel(rRectangle
.width
, priv
->m_fZoom
);
1126 aRectanglePixels
.height
= twipToPixel(rRectangle
.height
, priv
->m_fZoom
);
1128 aStart
.x
= aRectanglePixels
.y
/ nTileSizePixels
;
1129 aStart
.y
= aRectanglePixels
.x
/ nTileSizePixels
;
1130 aEnd
.x
= (aRectanglePixels
.y
+ aRectanglePixels
.height
+ nTileSizePixels
) / nTileSizePixels
;
1131 aEnd
.y
= (aRectanglePixels
.x
+ aRectanglePixels
.width
+ nTileSizePixels
) / nTileSizePixels
;
1132 for (int i
= aStart
.x
; i
< aEnd
.x
; i
++)
1134 for (int j
= aStart
.y
; j
< aEnd
.y
; j
++)
1136 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
1137 priv
->m_pTileBuffer
->setInvalid(i
, j
, priv
->m_fZoom
, task
, priv
->lokThreadPool
);
1138 g_object_unref(task
);
1144 callback (gpointer pData
)
1146 CallbackData
* pCallback
= static_cast<CallbackData
*>(pData
);
1147 LOKDocView
* pDocView
= LOK_DOC_VIEW (pCallback
->m_pDocView
);
1148 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1150 //callback registered before the widget was destroyed.
1151 //Use existance of lokThreadPool as flag it was torn down
1152 if (!priv
->lokThreadPool
)
1155 return G_SOURCE_REMOVE
;
1158 switch (pCallback
->m_nType
)
1160 case LOK_CALLBACK_INVALIDATE_TILES
:
1162 if (pCallback
->m_aPayload
.compare(0, 5, "EMPTY") != 0) // payload doesn't start with "EMPTY"
1164 GdkRectangle aRectangle
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1165 setTilesInvalid(pDocView
, aRectangle
);
1168 priv
->m_pTileBuffer
->resetAllTiles();
1170 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1173 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
:
1175 priv
->m_aVisibleCursor
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1176 priv
->m_bCursorOverlayVisible
= true;
1177 g_signal_emit(pDocView
, doc_view_signals
[CURSOR_CHANGED
], 0,
1178 priv
->m_aVisibleCursor
.x
,
1179 priv
->m_aVisibleCursor
.y
,
1180 priv
->m_aVisibleCursor
.width
,
1181 priv
->m_aVisibleCursor
.height
);
1182 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1185 case LOK_CALLBACK_TEXT_SELECTION
:
1187 priv
->m_aTextSelectionRectangles
= payloadToRectangles(pDocView
, pCallback
->m_aPayload
.c_str());
1188 gboolean bIsTextSelected
= !priv
->m_aTextSelectionRectangles
.empty();
1189 // In case the selection is empty, then we get no LOK_CALLBACK_TEXT_SELECTION_START/END events.
1190 if (!bIsTextSelected
)
1192 memset(&priv
->m_aTextSelectionStart
, 0, sizeof(priv
->m_aTextSelectionStart
));
1193 memset(&priv
->m_aHandleStartRect
, 0, sizeof(priv
->m_aHandleStartRect
));
1194 memset(&priv
->m_aTextSelectionEnd
, 0, sizeof(priv
->m_aTextSelectionEnd
));
1195 memset(&priv
->m_aHandleEndRect
, 0, sizeof(priv
->m_aHandleEndRect
));
1198 memset(&priv
->m_aHandleMiddleRect
, 0, sizeof(priv
->m_aHandleMiddleRect
));
1200 g_signal_emit(pDocView
, doc_view_signals
[TEXT_SELECTION
], 0, bIsTextSelected
);
1201 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1204 case LOK_CALLBACK_TEXT_SELECTION_START
:
1206 priv
->m_aTextSelectionStart
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1209 case LOK_CALLBACK_TEXT_SELECTION_END
:
1211 priv
->m_aTextSelectionEnd
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1214 case LOK_CALLBACK_CURSOR_VISIBLE
:
1216 priv
->m_bCursorVisible
= pCallback
->m_aPayload
== "true";
1219 case LOK_CALLBACK_MOUSE_POINTER
:
1221 // We do not want the cursor to get changed in view-only mode
1224 // The gtk docs claim that most css cursors should be supported, however
1225 // on my system at least this is not true and many cursors are unsupported.
1226 // In this case pCursor = null, which results in the default cursor
1228 GdkCursor
* pCursor
= gdk_cursor_new_from_name(gtk_widget_get_display(GTK_WIDGET(pDocView
)),
1229 pCallback
->m_aPayload
.c_str());
1230 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(pDocView
)), pCursor
);
1234 case LOK_CALLBACK_GRAPHIC_SELECTION
:
1236 if (pCallback
->m_aPayload
!= "EMPTY")
1237 priv
->m_aGraphicSelection
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1239 memset(&priv
->m_aGraphicSelection
, 0, sizeof(priv
->m_aGraphicSelection
));
1240 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1243 case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION
:
1245 std::stringstream
aStream(pCallback
->m_aPayload
);
1246 boost::property_tree::ptree aTree
;
1247 boost::property_tree::read_json(aStream
, aTree
);
1248 int nViewId
= aTree
.get
<int>("viewId");
1249 int nPart
= aTree
.get
<int>("part");
1250 const std::string
& rRectangle
= aTree
.get
<std::string
>("selection");
1251 if (rRectangle
!= "EMPTY")
1252 priv
->m_aGraphicViewSelections
[nViewId
] = ViewRectangle(nPart
, payloadToRectangle(pDocView
, rRectangle
.c_str()));
1255 auto it
= priv
->m_aGraphicViewSelections
.find(nViewId
);
1256 if (it
!= priv
->m_aGraphicViewSelections
.end())
1257 priv
->m_aGraphicViewSelections
.erase(it
);
1259 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1263 case LOK_CALLBACK_CELL_CURSOR
:
1265 if (pCallback
->m_aPayload
!= "EMPTY")
1266 priv
->m_aCellCursor
= payloadToRectangle(pDocView
, pCallback
->m_aPayload
.c_str());
1268 memset(&priv
->m_aCellCursor
, 0, sizeof(priv
->m_aCellCursor
));
1269 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1272 case LOK_CALLBACK_HYPERLINK_CLICKED
:
1274 hyperlinkClicked(pDocView
, pCallback
->m_aPayload
);
1277 case LOK_CALLBACK_STATE_CHANGED
:
1279 commandChanged(pDocView
, pCallback
->m_aPayload
);
1282 case LOK_CALLBACK_SEARCH_NOT_FOUND
:
1284 searchNotFound(pDocView
, pCallback
->m_aPayload
);
1287 case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED
:
1289 if (!pCallback
->m_aPayload
.empty())
1290 payloadToSize(pCallback
->m_aPayload
.c_str(), priv
->m_nDocumentWidthTwips
, priv
->m_nDocumentHeightTwips
);
1292 priv
->m_pDocument
->pClass
->getDocumentSize(priv
->m_pDocument
, &priv
->m_nDocumentWidthTwips
, &priv
->m_nDocumentHeightTwips
);
1294 gtk_widget_set_size_request(GTK_WIDGET(pDocView
),
1295 twipToPixel(priv
->m_nDocumentWidthTwips
, priv
->m_fZoom
),
1296 twipToPixel(priv
->m_nDocumentHeightTwips
, priv
->m_fZoom
));
1298 g_signal_emit(pDocView
, doc_view_signals
[SIZE_CHANGED
], 0, nullptr);
1301 case LOK_CALLBACK_SET_PART
:
1303 setPart(pDocView
, pCallback
->m_aPayload
);
1306 case LOK_CALLBACK_SEARCH_RESULT_SELECTION
:
1308 boost::property_tree::ptree aTree
;
1309 std::stringstream
aStream(pCallback
->m_aPayload
);
1310 boost::property_tree::read_json(aStream
, aTree
);
1311 int nCount
= aTree
.get_child("searchResultSelection").size();
1312 searchResultCount(pDocView
, std::to_string(nCount
));
1315 case LOK_CALLBACK_UNO_COMMAND_RESULT
:
1317 commandResult(pDocView
, pCallback
->m_aPayload
);
1320 case LOK_CALLBACK_CELL_ADDRESS
:
1322 addressChanged(pDocView
, pCallback
->m_aPayload
);
1325 case LOK_CALLBACK_CELL_FORMULA
:
1327 formulaChanged(pDocView
, pCallback
->m_aPayload
);
1330 case LOK_CALLBACK_ERROR
:
1332 reportError(pDocView
, pCallback
->m_aPayload
);
1335 case LOK_CALLBACK_CONTEXT_MENU
:
1337 // TODO: Implement me
1340 case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR
:
1342 std::stringstream
aStream(pCallback
->m_aPayload
);
1343 boost::property_tree::ptree aTree
;
1344 boost::property_tree::read_json(aStream
, aTree
);
1345 int nViewId
= aTree
.get
<int>("viewId");
1346 int nPart
= aTree
.get
<int>("part");
1347 const std::string
& rRectangle
= aTree
.get
<std::string
>("rectangle");
1348 priv
->m_aViewCursors
[nViewId
] = ViewRectangle(nPart
, payloadToRectangle(pDocView
, rRectangle
.c_str()));
1349 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1352 case LOK_CALLBACK_TEXT_VIEW_SELECTION
:
1354 std::stringstream
aStream(pCallback
->m_aPayload
);
1355 boost::property_tree::ptree aTree
;
1356 boost::property_tree::read_json(aStream
, aTree
);
1357 int nViewId
= aTree
.get
<int>("viewId");
1358 int nPart
= aTree
.get
<int>("part");
1359 const std::string
& rSelection
= aTree
.get
<std::string
>("selection");
1360 priv
->m_aTextViewSelectionRectangles
[nViewId
] = ViewRectangles(nPart
, payloadToRectangles(pDocView
, rSelection
.c_str()));
1361 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1364 case LOK_CALLBACK_VIEW_CURSOR_VISIBLE
:
1366 std::stringstream
aStream(pCallback
->m_aPayload
);
1367 boost::property_tree::ptree aTree
;
1368 boost::property_tree::read_json(aStream
, aTree
);
1369 int nViewId
= aTree
.get
<int>("viewId");
1370 const std::string
& rVisible
= aTree
.get
<std::string
>("visible");
1371 priv
->m_aViewCursorVisibilities
[nViewId
] = rVisible
== "true";
1372 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1376 case LOK_CALLBACK_CELL_VIEW_CURSOR
:
1378 std::stringstream
aStream(pCallback
->m_aPayload
);
1379 boost::property_tree::ptree aTree
;
1380 boost::property_tree::read_json(aStream
, aTree
);
1381 int nViewId
= aTree
.get
<int>("viewId");
1382 int nPart
= aTree
.get
<int>("part");
1383 const std::string
& rRectangle
= aTree
.get
<std::string
>("rectangle");
1384 if (rRectangle
!= "EMPTY")
1385 priv
->m_aCellViewCursors
[nViewId
] = ViewRectangle(nPart
, payloadToRectangle(pDocView
, rRectangle
.c_str()));
1388 auto it
= priv
->m_aCellViewCursors
.find(nViewId
);
1389 if (it
!= priv
->m_aCellViewCursors
.end())
1390 priv
->m_aCellViewCursors
.erase(it
);
1392 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1395 case LOK_CALLBACK_VIEW_LOCK
:
1397 std::stringstream
aStream(pCallback
->m_aPayload
);
1398 boost::property_tree::ptree aTree
;
1399 boost::property_tree::read_json(aStream
, aTree
);
1400 int nViewId
= aTree
.get
<int>("viewId");
1401 int nPart
= aTree
.get
<int>("part");
1402 const std::string
& rRectangle
= aTree
.get
<std::string
>("rectangle");
1403 if (rRectangle
!= "EMPTY")
1404 priv
->m_aViewLockRectangles
[nViewId
] = ViewRectangle(nPart
, payloadToRectangle(pDocView
, rRectangle
.c_str()));
1407 auto it
= priv
->m_aViewLockRectangles
.find(nViewId
);
1408 if (it
!= priv
->m_aViewLockRectangles
.end())
1409 priv
->m_aViewLockRectangles
.erase(it
);
1411 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
1414 case LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED
:
1418 case LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED
:
1422 case LOK_CALLBACK_COMMENT
:
1423 g_signal_emit(pCallback
->m_pDocView
, doc_view_signals
[COMMENT
], 0, pCallback
->m_aPayload
.c_str());
1431 return G_SOURCE_REMOVE
;
1434 static void callbackWorker (int nType
, const char* pPayload
, void* pData
)
1436 LOKDocView
* pDocView
= LOK_DOC_VIEW (pData
);
1438 CallbackData
* pCallback
= new CallbackData(nType
, pPayload
? pPayload
: "(nil)", pDocView
);
1439 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1440 std::stringstream ss
;
1441 ss
<< "callbackWorker, view #" << priv
->m_nViewId
<< ": " << callbackTypeToString(nType
) << ", '" << (pPayload
? pPayload
: "(nil)") << "'";
1442 g_info("%s", ss
.str().c_str());
1443 gdk_threads_add_idle(callback
, pCallback
);
1447 renderHandle(LOKDocView
* pDocView
,
1449 const GdkRectangle
& rCursor
,
1450 cairo_surface_t
* pHandle
,
1451 GdkRectangle
& rRectangle
)
1453 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1454 GdkPoint aCursorBottom
;
1455 int nHandleWidth
, nHandleHeight
;
1456 double fHandleScale
;
1458 nHandleWidth
= cairo_image_surface_get_width(pHandle
);
1459 nHandleHeight
= cairo_image_surface_get_height(pHandle
);
1460 // We want to scale down the handle, so that its height is the same as the cursor caret.
1461 fHandleScale
= twipToPixel(rCursor
.height
, priv
->m_fZoom
) / nHandleHeight
;
1462 // We want the top center of the handle bitmap to be at the bottom center of the cursor rectangle.
1463 aCursorBottom
.x
= twipToPixel(rCursor
.x
, priv
->m_fZoom
) + twipToPixel(rCursor
.width
, priv
->m_fZoom
) / 2 - (nHandleWidth
* fHandleScale
) / 2;
1464 aCursorBottom
.y
= twipToPixel(rCursor
.y
, priv
->m_fZoom
) + twipToPixel(rCursor
.height
, priv
->m_fZoom
);
1466 cairo_save (pCairo
);
1467 cairo_translate(pCairo
, aCursorBottom
.x
, aCursorBottom
.y
);
1468 cairo_scale(pCairo
, fHandleScale
, fHandleScale
);
1469 cairo_set_source_surface(pCairo
, pHandle
, 0, 0);
1470 cairo_paint(pCairo
);
1471 cairo_restore (pCairo
);
1473 rRectangle
.x
= aCursorBottom
.x
;
1474 rRectangle
.y
= aCursorBottom
.y
;
1475 rRectangle
.width
= nHandleWidth
* fHandleScale
;
1476 rRectangle
.height
= nHandleHeight
* fHandleScale
;
1479 /// Renders handles around an rSelection rectangle on pCairo.
1481 renderGraphicHandle(LOKDocView
* pDocView
,
1483 const GdkRectangle
& rSelection
,
1484 const GdkRGBA
& rColor
)
1486 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1487 int nHandleWidth
= 9, nHandleHeight
= 9;
1488 GdkRectangle aSelection
;
1490 aSelection
.x
= twipToPixel(rSelection
.x
, priv
->m_fZoom
);
1491 aSelection
.y
= twipToPixel(rSelection
.y
, priv
->m_fZoom
);
1492 aSelection
.width
= twipToPixel(rSelection
.width
, priv
->m_fZoom
);
1493 aSelection
.height
= twipToPixel(rSelection
.height
, priv
->m_fZoom
);
1495 for (int i
= 0; i
< GRAPHIC_HANDLE_COUNT
; ++i
)
1497 int x
= aSelection
.x
, y
= aSelection
.y
;
1503 case 1: // top-middle
1504 x
+= aSelection
.width
/ 2;
1506 case 2: // top-right
1507 x
+= aSelection
.width
;
1509 case 3: // middle-left
1510 y
+= aSelection
.height
/ 2;
1512 case 4: // middle-right
1513 x
+= aSelection
.width
;
1514 y
+= aSelection
.height
/ 2;
1516 case 5: // bottom-left
1517 y
+= aSelection
.height
;
1519 case 6: // bottom-middle
1520 x
+= aSelection
.width
/ 2;
1521 y
+= aSelection
.height
;
1523 case 7: // bottom-right
1524 x
+= aSelection
.width
;
1525 y
+= aSelection
.height
;
1529 // Center the handle.
1530 x
-= nHandleWidth
/ 2;
1531 y
-= nHandleHeight
/ 2;
1533 priv
->m_aGraphicHandleRects
[i
].x
= x
;
1534 priv
->m_aGraphicHandleRects
[i
].y
= y
;
1535 priv
->m_aGraphicHandleRects
[i
].width
= nHandleWidth
;
1536 priv
->m_aGraphicHandleRects
[i
].height
= nHandleHeight
;
1538 cairo_set_source_rgb(pCairo
, rColor
.red
, rColor
.green
, rColor
.blue
);
1539 cairo_rectangle(pCairo
, x
, y
, nHandleWidth
, nHandleHeight
);
1544 /// Finishes the paint tile operation and returns the result, if any
1546 paintTileFinish(LOKDocView
* pDocView
, GAsyncResult
* res
, GError
**error
)
1548 GTask
* task
= G_TASK(res
);
1550 g_return_val_if_fail(LOK_IS_DOC_VIEW(pDocView
), nullptr);
1551 g_return_val_if_fail(g_task_is_valid(res
, pDocView
), nullptr);
1552 g_return_val_if_fail(error
== nullptr || *error
== nullptr, nullptr);
1554 return g_task_propagate_pointer(task
, error
);
1557 /// Callback called in the main UI thread when paintTileInThread in LOK thread has finished
1559 paintTileCallback(GObject
* sourceObject
, GAsyncResult
* res
, gpointer userData
)
1561 LOKDocView
* pDocView
= LOK_DOC_VIEW(sourceObject
);
1562 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1563 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(userData
);
1564 std::unique_ptr
<TileBuffer
>& buffer
= priv
->m_pTileBuffer
;
1565 int index
= pLOEvent
->m_nPaintTileX
* buffer
->m_nWidth
+ pLOEvent
->m_nPaintTileY
;
1569 cairo_surface_t
* pSurface
= static_cast<cairo_surface_t
*>(paintTileFinish(pDocView
, res
, &error
));
1570 if (error
!= nullptr)
1572 if (error
->domain
== LOK_TILEBUFFER_ERROR
&&
1573 error
->code
== LOK_TILEBUFFER_CHANGED
)
1574 g_info("Skipping paint tile request because corresponding"
1575 "tile buffer has been destroyed");
1577 g_warning("Unable to get painted GdkPixbuf: %s", error
->message
);
1578 g_error_free(error
);
1582 buffer
->m_mTiles
[index
].setSurface(pSurface
);
1583 buffer
->m_mTiles
[index
].valid
= true;
1584 gdk_threads_add_idle(queueDraw
, GTK_WIDGET(pDocView
));
1586 cairo_surface_destroy(pSurface
);
1591 renderDocument(LOKDocView
* pDocView
, cairo_t
* pCairo
)
1593 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1594 GdkRectangle aVisibleArea
;
1595 long nDocumentWidthPixels
= twipToPixel(priv
->m_nDocumentWidthTwips
, priv
->m_fZoom
);
1596 long nDocumentHeightPixels
= twipToPixel(priv
->m_nDocumentHeightTwips
, priv
->m_fZoom
);
1597 // Total number of rows / columns in this document.
1598 guint nRows
= ceil((double)nDocumentHeightPixels
/ nTileSizePixels
);
1599 guint nColumns
= ceil((double)nDocumentWidthPixels
/ nTileSizePixels
);
1601 gdk_cairo_get_clip_rectangle (pCairo
, &aVisibleArea
);
1602 aVisibleArea
.x
= pixelToTwip (aVisibleArea
.x
, priv
->m_fZoom
);
1603 aVisibleArea
.y
= pixelToTwip (aVisibleArea
.y
, priv
->m_fZoom
);
1604 aVisibleArea
.width
= pixelToTwip (aVisibleArea
.width
, priv
->m_fZoom
);
1605 aVisibleArea
.height
= pixelToTwip (aVisibleArea
.height
, priv
->m_fZoom
);
1607 // Render the tiles.
1608 for (guint nRow
= 0; nRow
< nRows
; ++nRow
)
1610 for (guint nColumn
= 0; nColumn
< nColumns
; ++nColumn
)
1612 GdkRectangle aTileRectangleTwips
, aTileRectanglePixels
;
1615 // Determine size of the tile: the rightmost/bottommost tiles may
1616 // be smaller, and we need the size to decide if we need to repaint.
1617 if (nColumn
== nColumns
- 1)
1618 aTileRectanglePixels
.width
= nDocumentWidthPixels
- nColumn
* nTileSizePixels
;
1620 aTileRectanglePixels
.width
= nTileSizePixels
;
1621 if (nRow
== nRows
- 1)
1622 aTileRectanglePixels
.height
= nDocumentHeightPixels
- nRow
* nTileSizePixels
;
1624 aTileRectanglePixels
.height
= nTileSizePixels
;
1626 // Determine size and position of the tile in document coordinates,
1627 // so we can decide if we can skip painting for partial rendering.
1628 aTileRectangleTwips
.x
= pixelToTwip(nTileSizePixels
, priv
->m_fZoom
) * nColumn
;
1629 aTileRectangleTwips
.y
= pixelToTwip(nTileSizePixels
, priv
->m_fZoom
) * nRow
;
1630 aTileRectangleTwips
.width
= pixelToTwip(aTileRectanglePixels
.width
, priv
->m_fZoom
);
1631 aTileRectangleTwips
.height
= pixelToTwip(aTileRectanglePixels
.height
, priv
->m_fZoom
);
1633 if (!gdk_rectangle_intersect(&aVisibleArea
, &aTileRectangleTwips
, nullptr))
1638 LOEvent
* pLOEvent
= new LOEvent(LOK_PAINT_TILE
);
1639 pLOEvent
->m_nPaintTileX
= nRow
;
1640 pLOEvent
->m_nPaintTileY
= nColumn
;
1641 pLOEvent
->m_fPaintTileZoom
= priv
->m_fZoom
;
1642 pLOEvent
->m_pTileBuffer
= &*priv
->m_pTileBuffer
;
1643 GTask
* task
= g_task_new(pDocView
, nullptr, paintTileCallback
, pLOEvent
);
1644 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
1646 Tile
& currentTile
= priv
->m_pTileBuffer
->getTile(nRow
, nColumn
, task
, priv
->lokThreadPool
);
1647 cairo_surface_t
* pSurface
= currentTile
.getBuffer();
1648 cairo_set_source_surface(pCairo
, pSurface
,
1649 twipToPixel(aTileRectangleTwips
.x
, priv
->m_fZoom
),
1650 twipToPixel(aTileRectangleTwips
.y
, priv
->m_fZoom
));
1651 cairo_paint(pCairo
);
1652 g_object_unref(task
);
1660 static const GdkRGBA
& getDarkColor(int nViewId
, LOKDocViewPrivate
& priv
)
1662 static std::map
<int, GdkRGBA
> aColorMap
;
1663 auto it
= aColorMap
.find(nViewId
);
1664 if (it
!= aColorMap
.end())
1667 if (priv
->m_eDocumentType
== LOK_DOCTYPE_TEXT
)
1669 char* pValues
= priv
->m_pDocument
->pClass
->getCommandValues(priv
->m_pDocument
, ".uno:TrackedChangeAuthors");
1670 std::stringstream aInfo
;
1671 aInfo
<< "lok::Document::getCommandValues('.uno:TrackedChangeAuthors') returned '" << pValues
<< "'" << std::endl
;
1672 g_info("%s", aInfo
.str().c_str());
1674 std::stringstream
aStream(pValues
);
1675 boost::property_tree::ptree aTree
;
1676 boost::property_tree::read_json(aStream
, aTree
);
1677 for (const auto& rValue
: aTree
.get_child("authors"))
1679 const std::string
& rName
= rValue
.second
.get
<std::string
>("name");
1680 guint32 nColor
= rValue
.second
.get
<guint32
>("color");
1681 GdkRGBA aColor
{((double)((guint8
)((nColor
)>>16)))/255, ((double)((guint8
)(((guint16
)(nColor
)) >> 8)))/255, ((double)((guint8
)(nColor
)))/255, 0};
1682 auto itAuthorViews
= g_aAuthorViews
.find(rName
);
1683 if (itAuthorViews
!= g_aAuthorViews
.end())
1684 aColorMap
[itAuthorViews
->second
] = aColor
;
1689 // Based on tools/colordata.hxx, COL_AUTHOR1_DARK..COL_AUTHOR9_DARK.
1690 static std::vector
<GdkRGBA
> aColors
=
1692 {((double)198)/255, ((double)146)/255, ((double)0)/255, 0},
1693 {((double)6)/255, ((double)70)/255, ((double)162)/255, 0},
1694 {((double)87)/255, ((double)157)/255, ((double)28)/255, 0},
1695 {((double)105)/255, ((double)43)/255, ((double)157)/255, 0},
1696 {((double)197)/255, ((double)0)/255, ((double)11)/255, 0},
1697 {((double)0)/255, ((double)128)/255, ((double)128)/255, 0},
1698 {((double)140)/255, ((double)132)/255, ((double)0)/255, 0},
1699 {((double)43)/255, ((double)85)/255, ((double)107)/255, 0},
1700 {((double)209)/255, ((double)118)/255, ((double)0)/255, 0},
1702 static int nColorCounter
= 0;
1703 GdkRGBA aColor
= aColors
[nColorCounter
++ % aColors
.size()];
1704 aColorMap
[nViewId
] = aColor
;
1706 assert(aColorMap
.find(nViewId
) != aColorMap
.end());
1707 return aColorMap
[nViewId
];
1711 renderOverlay(LOKDocView
* pDocView
, cairo_t
* pCairo
)
1713 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1715 if (priv
->m_bEdit
&& priv
->m_bCursorVisible
&& priv
->m_bCursorOverlayVisible
&& !isEmptyRectangle(priv
->m_aVisibleCursor
))
1717 if (priv
->m_aVisibleCursor
.width
< 30)
1718 // Set a minimal width if it would be 0.
1719 priv
->m_aVisibleCursor
.width
= 30;
1721 cairo_set_source_rgb(pCairo
, 0, 0, 0);
1722 cairo_rectangle(pCairo
,
1723 twipToPixel(priv
->m_aVisibleCursor
.x
, priv
->m_fZoom
),
1724 twipToPixel(priv
->m_aVisibleCursor
.y
, priv
->m_fZoom
),
1725 twipToPixel(priv
->m_aVisibleCursor
.width
, priv
->m_fZoom
),
1726 twipToPixel(priv
->m_aVisibleCursor
.height
, priv
->m_fZoom
));
1730 // View cursors: they do not blink and are colored.
1731 if (priv
->m_bEdit
&& !priv
->m_aViewCursors
.empty())
1733 for (auto& rPair
: priv
->m_aViewCursors
)
1735 auto itVisibility
= priv
->m_aViewCursorVisibilities
.find(rPair
.first
);
1736 if (itVisibility
!= priv
->m_aViewCursorVisibilities
.end() && !itVisibility
->second
)
1739 // Show view cursors when in Writer or when the part matches.
1740 if (rPair
.second
.m_nPart
!= priv
->m_nPartId
&& priv
->m_eDocumentType
!= LOK_DOCTYPE_TEXT
)
1743 GdkRectangle
& rCursor
= rPair
.second
.m_aRectangle
;
1744 if (rCursor
.width
< 30)
1745 // Set a minimal width if it would be 0.
1748 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
1749 cairo_set_source_rgb(pCairo
, rDark
.red
, rDark
.green
, rDark
.blue
);
1750 cairo_rectangle(pCairo
,
1751 twipToPixel(rCursor
.x
, priv
->m_fZoom
),
1752 twipToPixel(rCursor
.y
, priv
->m_fZoom
),
1753 twipToPixel(rCursor
.width
, priv
->m_fZoom
),
1754 twipToPixel(rCursor
.height
, priv
->m_fZoom
));
1759 if (priv
->m_bEdit
&& priv
->m_bCursorVisible
&& !isEmptyRectangle(priv
->m_aVisibleCursor
) && priv
->m_aTextSelectionRectangles
.empty())
1761 // Have a cursor, but no selection: we need the middle handle.
1762 gchar
* handleMiddlePath
= g_strconcat (priv
->m_aLOPath
, CURSOR_HANDLE_DIR
, "handle_image_middle.png", nullptr);
1763 if (!priv
->m_pHandleMiddle
)
1765 priv
->m_pHandleMiddle
= cairo_image_surface_create_from_png(handleMiddlePath
);
1766 assert(cairo_surface_status(priv
->m_pHandleMiddle
) == CAIRO_STATUS_SUCCESS
);
1768 g_free (handleMiddlePath
);
1769 renderHandle(pDocView
, pCairo
, priv
->m_aVisibleCursor
, priv
->m_pHandleMiddle
, priv
->m_aHandleMiddleRect
);
1772 if (!priv
->m_aTextSelectionRectangles
.empty())
1774 for (GdkRectangle
& rRectangle
: priv
->m_aTextSelectionRectangles
)
1776 // Blue with 75% transparency.
1777 cairo_set_source_rgba(pCairo
, ((double)0x43)/255, ((double)0xac)/255, ((double)0xe8)/255, 0.25);
1778 cairo_rectangle(pCairo
,
1779 twipToPixel(rRectangle
.x
, priv
->m_fZoom
),
1780 twipToPixel(rRectangle
.y
, priv
->m_fZoom
),
1781 twipToPixel(rRectangle
.width
, priv
->m_fZoom
),
1782 twipToPixel(rRectangle
.height
, priv
->m_fZoom
));
1787 if (!isEmptyRectangle(priv
->m_aTextSelectionStart
))
1789 // Have a start position: we need a start handle.
1790 gchar
* handleStartPath
= g_strconcat (priv
->m_aLOPath
, CURSOR_HANDLE_DIR
, "handle_image_start.png", nullptr);
1791 if (!priv
->m_pHandleStart
)
1793 priv
->m_pHandleStart
= cairo_image_surface_create_from_png(handleStartPath
);
1794 assert(cairo_surface_status(priv
->m_pHandleStart
) == CAIRO_STATUS_SUCCESS
);
1796 renderHandle(pDocView
, pCairo
, priv
->m_aTextSelectionStart
, priv
->m_pHandleStart
, priv
->m_aHandleStartRect
);
1797 g_free (handleStartPath
);
1799 if (!isEmptyRectangle(priv
->m_aTextSelectionEnd
))
1801 // Have a start position: we need an end handle.
1802 gchar
* handleEndPath
= g_strconcat (priv
->m_aLOPath
, CURSOR_HANDLE_DIR
, "handle_image_end.png", nullptr);
1803 if (!priv
->m_pHandleEnd
)
1805 priv
->m_pHandleEnd
= cairo_image_surface_create_from_png(handleEndPath
);
1806 assert(cairo_surface_status(priv
->m_pHandleEnd
) == CAIRO_STATUS_SUCCESS
);
1808 renderHandle(pDocView
, pCairo
, priv
->m_aTextSelectionEnd
, priv
->m_pHandleEnd
, priv
->m_aHandleEndRect
);
1809 g_free (handleEndPath
);
1813 // Selections of other views.
1814 for (auto& rPair
: priv
->m_aTextViewSelectionRectangles
)
1816 if (rPair
.second
.m_nPart
!= priv
->m_nPartId
&& priv
->m_eDocumentType
!= LOK_DOCTYPE_TEXT
)
1819 for (GdkRectangle
& rRectangle
: rPair
.second
.m_aRectangles
)
1821 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
1822 // 75% transparency.
1823 cairo_set_source_rgba(pCairo
, rDark
.red
, rDark
.green
, rDark
.blue
, 0.25);
1824 cairo_rectangle(pCairo
,
1825 twipToPixel(rRectangle
.x
, priv
->m_fZoom
),
1826 twipToPixel(rRectangle
.y
, priv
->m_fZoom
),
1827 twipToPixel(rRectangle
.width
, priv
->m_fZoom
),
1828 twipToPixel(rRectangle
.height
, priv
->m_fZoom
));
1833 if (!isEmptyRectangle(priv
->m_aGraphicSelection
))
1835 GdkRGBA aBlack
{0, 0, 0, 0};
1836 renderGraphicHandle(pDocView
, pCairo
, priv
->m_aGraphicSelection
, aBlack
);
1839 // Graphic selections of other views.
1840 for (auto& rPair
: priv
->m_aGraphicViewSelections
)
1842 const ViewRectangle
& rRectangle
= rPair
.second
;
1843 if (rRectangle
.m_nPart
!= priv
->m_nPartId
&& priv
->m_eDocumentType
!= LOK_DOCTYPE_TEXT
)
1846 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
1847 renderGraphicHandle(pDocView
, pCairo
, rRectangle
.m_aRectangle
, rDark
);
1850 // Draw the cell cursor.
1851 if (!isEmptyRectangle(priv
->m_aCellCursor
))
1853 cairo_set_source_rgb(pCairo
, 0, 0, 0);
1854 cairo_rectangle(pCairo
,
1855 twipToPixel(priv
->m_aCellCursor
.x
, priv
->m_fZoom
),
1856 twipToPixel(priv
->m_aCellCursor
.y
, priv
->m_fZoom
),
1857 twipToPixel(priv
->m_aCellCursor
.width
, priv
->m_fZoom
),
1858 twipToPixel(priv
->m_aCellCursor
.height
, priv
->m_fZoom
));
1859 cairo_set_line_width(pCairo
, 2.0);
1860 cairo_stroke(pCairo
);
1863 // Cell view cursors: they are colored.
1864 for (auto& rPair
: priv
->m_aCellViewCursors
)
1866 const ViewRectangle
& rCursor
= rPair
.second
;
1867 if (rCursor
.m_nPart
!= priv
->m_nPartId
)
1870 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
1871 cairo_set_source_rgb(pCairo
, rDark
.red
, rDark
.green
, rDark
.blue
);
1872 cairo_rectangle(pCairo
,
1873 twipToPixel(rCursor
.m_aRectangle
.x
, priv
->m_fZoom
),
1874 twipToPixel(rCursor
.m_aRectangle
.y
, priv
->m_fZoom
),
1875 twipToPixel(rCursor
.m_aRectangle
.width
, priv
->m_fZoom
),
1876 twipToPixel(rCursor
.m_aRectangle
.height
, priv
->m_fZoom
));
1877 cairo_set_line_width(pCairo
, 2.0);
1878 cairo_stroke(pCairo
);
1881 // View locks: they are colored.
1882 for (auto& rPair
: priv
->m_aViewLockRectangles
)
1884 const ViewRectangle
& rRectangle
= rPair
.second
;
1885 if (rRectangle
.m_nPart
!= priv
->m_nPartId
)
1888 // Draw a rectangle.
1889 const GdkRGBA
& rDark
= getDarkColor(rPair
.first
, priv
);
1890 cairo_set_source_rgb(pCairo
, rDark
.red
, rDark
.green
, rDark
.blue
);
1891 cairo_rectangle(pCairo
,
1892 twipToPixel(rRectangle
.m_aRectangle
.x
, priv
->m_fZoom
),
1893 twipToPixel(rRectangle
.m_aRectangle
.y
, priv
->m_fZoom
),
1894 twipToPixel(rRectangle
.m_aRectangle
.width
, priv
->m_fZoom
),
1895 twipToPixel(rRectangle
.m_aRectangle
.height
, priv
->m_fZoom
));
1896 cairo_set_line_width(pCairo
, 2.0);
1897 cairo_stroke(pCairo
);
1900 cairo_rectangle(pCairo
,
1901 twipToPixel(rRectangle
.m_aRectangle
.x
+ rRectangle
.m_aRectangle
.width
, priv
->m_fZoom
) - 25,
1902 twipToPixel(rRectangle
.m_aRectangle
.y
+ rRectangle
.m_aRectangle
.height
, priv
->m_fZoom
) - 15,
1907 twipToPixel(rRectangle
.m_aRectangle
.x
+ rRectangle
.m_aRectangle
.width
, priv
->m_fZoom
) - 15,
1908 twipToPixel(rRectangle
.m_aRectangle
.y
+ rRectangle
.m_aRectangle
.height
, priv
->m_fZoom
) - 15,
1910 180.0 * (M_PI
/180.0),
1911 360.0 * (M_PI
/180.0));
1912 cairo_stroke(pCairo
);
1919 lok_doc_view_signal_button(GtkWidget
* pWidget
, GdkEventButton
* pEvent
)
1921 LOKDocView
* pDocView
= LOK_DOC_VIEW (pWidget
);
1922 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
1923 GError
* error
= nullptr;
1925 g_info("LOKDocView_Impl::signalButton: %d, %d (in twips: %d, %d)",
1926 (int)pEvent
->x
, (int)pEvent
->y
,
1927 (int)pixelToTwip(pEvent
->x
, priv
->m_fZoom
),
1928 (int)pixelToTwip(pEvent
->y
, priv
->m_fZoom
));
1929 gtk_widget_grab_focus(GTK_WIDGET(pDocView
));
1931 switch (pEvent
->type
)
1933 case GDK_BUTTON_PRESS
:
1935 GdkRectangle aClick
;
1936 aClick
.x
= pEvent
->x
;
1937 aClick
.y
= pEvent
->y
;
1941 if (handleTextSelectionOnButtonPress(aClick
, pDocView
))
1943 if (handleGraphicSelectionOnButtonPress(aClick
, pDocView
))
1947 if ((pEvent
->time
- priv
->m_nLastButtonPressTime
) < 250)
1949 priv
->m_nLastButtonPressTime
= pEvent
->time
;
1950 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
1951 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_MOUSE_EVENT
);
1952 pLOEvent
->m_nPostMouseEventType
= LOK_MOUSEEVENT_MOUSEBUTTONDOWN
;
1953 pLOEvent
->m_nPostMouseEventX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
1954 pLOEvent
->m_nPostMouseEventY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
1955 pLOEvent
->m_nPostMouseEventCount
= nCount
;
1956 switch (pEvent
->button
)
1959 pLOEvent
->m_nPostMouseEventButton
= MOUSE_LEFT
;
1962 pLOEvent
->m_nPostMouseEventButton
= MOUSE_MIDDLE
;
1965 pLOEvent
->m_nPostMouseEventButton
= MOUSE_RIGHT
;
1968 pLOEvent
->m_nPostMouseEventModifier
= priv
->m_nKeyModifier
;
1969 priv
->m_nLastButtonPressed
= pLOEvent
->m_nPostMouseEventButton
;
1970 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
1972 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
1973 if (error
!= nullptr)
1975 g_warning("Unable to call LOK_POST_MOUSE_EVENT: %s", error
->message
);
1976 g_clear_error(&error
);
1978 g_object_unref(task
);
1981 case GDK_BUTTON_RELEASE
:
1983 if (handleTextSelectionOnButtonRelease(pDocView
))
1985 if (handleGraphicSelectionOnButtonRelease(pDocView
, pEvent
))
1989 if ((pEvent
->time
- priv
->m_nLastButtonReleaseTime
) < 250)
1991 priv
->m_nLastButtonReleaseTime
= pEvent
->time
;
1992 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
1993 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_MOUSE_EVENT
);
1994 pLOEvent
->m_nPostMouseEventType
= LOK_MOUSEEVENT_MOUSEBUTTONUP
;
1995 pLOEvent
->m_nPostMouseEventX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
1996 pLOEvent
->m_nPostMouseEventY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
1997 pLOEvent
->m_nPostMouseEventCount
= nCount
;
1998 switch (pEvent
->button
)
2001 pLOEvent
->m_nPostMouseEventButton
= MOUSE_LEFT
;
2004 pLOEvent
->m_nPostMouseEventButton
= MOUSE_MIDDLE
;
2007 pLOEvent
->m_nPostMouseEventButton
= MOUSE_RIGHT
;
2010 pLOEvent
->m_nPostMouseEventModifier
= priv
->m_nKeyModifier
;
2011 priv
->m_nLastButtonPressed
= pLOEvent
->m_nPostMouseEventButton
;
2012 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
2014 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
2015 if (error
!= nullptr)
2017 g_warning("Unable to call LOK_POST_MOUSE_EVENT: %s", error
->message
);
2018 g_clear_error(&error
);
2020 g_object_unref(task
);
2030 getDragPoint(GdkRectangle
* pHandle
,
2031 GdkEventMotion
* pEvent
,
2034 GdkPoint aCursor
, aHandle
;
2036 // Center of the cursor rectangle: we know that it's above the handle.
2037 aCursor
.x
= pHandle
->x
+ pHandle
->width
/ 2;
2038 aCursor
.y
= pHandle
->y
- pHandle
->height
/ 2;
2039 // Center of the handle rectangle.
2040 aHandle
.x
= pHandle
->x
+ pHandle
->width
/ 2;
2041 aHandle
.y
= pHandle
->y
+ pHandle
->height
/ 2;
2042 // Our target is the original cursor position + the dragged offset.
2043 pPoint
->x
= aCursor
.x
+ (pEvent
->x
- aHandle
.x
);
2044 pPoint
->y
= aCursor
.y
+ (pEvent
->y
- aHandle
.y
);
2048 lok_doc_view_signal_motion (GtkWidget
* pWidget
, GdkEventMotion
* pEvent
)
2050 LOKDocView
* pDocView
= LOK_DOC_VIEW (pWidget
);
2051 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2053 GError
* error
= nullptr;
2055 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2056 std::stringstream ss
;
2057 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2058 g_info("%s", ss
.str().c_str());
2059 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2060 if (priv
->m_bInDragMiddleHandle
)
2062 g_info("lcl_signalMotion: dragging the middle handle");
2063 getDragPoint(&priv
->m_aHandleMiddleRect
, pEvent
, &aPoint
);
2064 priv
->m_pDocument
->pClass
->setTextSelection(priv
->m_pDocument
, LOK_SETTEXTSELECTION_RESET
, pixelToTwip(aPoint
.x
, priv
->m_fZoom
), pixelToTwip(aPoint
.y
, priv
->m_fZoom
));
2067 if (priv
->m_bInDragStartHandle
)
2069 g_info("lcl_signalMotion: dragging the start handle");
2070 getDragPoint(&priv
->m_aHandleStartRect
, pEvent
, &aPoint
);
2071 priv
->m_pDocument
->pClass
->setTextSelection(priv
->m_pDocument
, LOK_SETTEXTSELECTION_START
, pixelToTwip(aPoint
.x
, priv
->m_fZoom
), pixelToTwip(aPoint
.y
, priv
->m_fZoom
));
2074 if (priv
->m_bInDragEndHandle
)
2076 g_info("lcl_signalMotion: dragging the end handle");
2077 getDragPoint(&priv
->m_aHandleEndRect
, pEvent
, &aPoint
);
2078 priv
->m_pDocument
->pClass
->setTextSelection(priv
->m_pDocument
, LOK_SETTEXTSELECTION_END
, pixelToTwip(aPoint
.x
, priv
->m_fZoom
), pixelToTwip(aPoint
.y
, priv
->m_fZoom
));
2082 for (int i
= 0; i
< GRAPHIC_HANDLE_COUNT
; ++i
)
2084 if (priv
->m_bInDragGraphicHandles
[i
])
2086 g_info("lcl_signalMotion: dragging the graphic handle #%d", i
);
2090 if (priv
->m_bInDragGraphicSelection
)
2092 g_info("lcl_signalMotion: dragging the graphic selection");
2096 GdkRectangle aMotionInTwipsInTwips
;
2097 aMotionInTwipsInTwips
.x
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
2098 aMotionInTwipsInTwips
.y
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
2099 aMotionInTwipsInTwips
.width
= 1;
2100 aMotionInTwipsInTwips
.height
= 1;
2101 if (gdk_rectangle_intersect(&aMotionInTwipsInTwips
, &priv
->m_aGraphicSelection
, nullptr))
2103 g_info("lcl_signalMotion: start of drag graphic selection");
2104 priv
->m_bInDragGraphicSelection
= true;
2106 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
2107 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_GRAPHIC_SELECTION
);
2108 pLOEvent
->m_nSetGraphicSelectionType
= LOK_SETGRAPHICSELECTION_START
;
2109 pLOEvent
->m_nSetGraphicSelectionX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
2110 pLOEvent
->m_nSetGraphicSelectionY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
2111 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
2113 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
2114 if (error
!= nullptr)
2116 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error
->message
);
2117 g_clear_error(&error
);
2119 g_object_unref(task
);
2124 // Otherwise a mouse move, as on the desktop.
2126 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
2127 LOEvent
* pLOEvent
= new LOEvent(LOK_POST_MOUSE_EVENT
);
2128 pLOEvent
->m_nPostMouseEventType
= LOK_MOUSEEVENT_MOUSEMOVE
;
2129 pLOEvent
->m_nPostMouseEventX
= pixelToTwip(pEvent
->x
, priv
->m_fZoom
);
2130 pLOEvent
->m_nPostMouseEventY
= pixelToTwip(pEvent
->y
, priv
->m_fZoom
);
2131 pLOEvent
->m_nPostMouseEventCount
= 1;
2132 pLOEvent
->m_nPostMouseEventButton
= priv
->m_nLastButtonPressed
;
2133 pLOEvent
->m_nPostMouseEventModifier
= priv
->m_nKeyModifier
;
2135 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
2137 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
2138 if (error
!= nullptr)
2140 g_warning("Unable to call LOK_MOUSEEVENT_MOUSEMOVE: %s", error
->message
);
2141 g_clear_error(&error
);
2143 g_object_unref(task
);
2149 setGraphicSelectionInThread(gpointer data
)
2151 GTask
* task
= G_TASK(data
);
2152 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2153 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2154 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2156 std::lock_guard
<std::mutex
> aGuard(g_aLOKMutex
);
2157 std::stringstream ss
;
2158 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2159 g_info("%s", ss
.str().c_str());
2160 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2161 ss
.str(std::string());
2162 ss
<< "lok::Document::setGraphicSelection(" << pLOEvent
->m_nSetGraphicSelectionType
;
2163 ss
<< ", " << pLOEvent
->m_nSetGraphicSelectionX
;
2164 ss
<< ", " << pLOEvent
->m_nSetGraphicSelectionY
<< ")";
2165 g_info("%s", ss
.str().c_str());
2166 priv
->m_pDocument
->pClass
->setGraphicSelection(priv
->m_pDocument
,
2167 pLOEvent
->m_nSetGraphicSelectionType
,
2168 pLOEvent
->m_nSetGraphicSelectionX
,
2169 pLOEvent
->m_nSetGraphicSelectionY
);
2173 setClientZoomInThread(gpointer data
)
2175 GTask
* task
= G_TASK(data
);
2176 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2177 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2178 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2180 std::lock_guard
<std::mutex
> aGuard(g_aLOKMutex
);
2181 priv
->m_pDocument
->pClass
->setClientZoom(priv
->m_pDocument
,
2182 pLOEvent
->m_nTilePixelWidth
,
2183 pLOEvent
->m_nTilePixelHeight
,
2184 pLOEvent
->m_nTileTwipWidth
,
2185 pLOEvent
->m_nTileTwipHeight
);
2189 postMouseEventInThread(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
));
2196 std::lock_guard
<std::mutex
> aGuard(g_aLOKMutex
);
2197 std::stringstream ss
;
2198 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2199 g_info("%s", ss
.str().c_str());
2200 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2201 ss
.str(std::string());
2202 ss
<< "lok::Document::postMouseEvent(" << pLOEvent
->m_nPostMouseEventType
;
2203 ss
<< ", " << pLOEvent
->m_nPostMouseEventX
;
2204 ss
<< ", " << pLOEvent
->m_nPostMouseEventY
;
2205 ss
<< ", " << pLOEvent
->m_nPostMouseEventCount
;
2206 ss
<< ", " << pLOEvent
->m_nPostMouseEventButton
;
2207 ss
<< ", " << pLOEvent
->m_nPostMouseEventModifier
<< ")";
2208 g_info("%s", ss
.str().c_str());
2209 priv
->m_pDocument
->pClass
->postMouseEvent(priv
->m_pDocument
,
2210 pLOEvent
->m_nPostMouseEventType
,
2211 pLOEvent
->m_nPostMouseEventX
,
2212 pLOEvent
->m_nPostMouseEventY
,
2213 pLOEvent
->m_nPostMouseEventCount
,
2214 pLOEvent
->m_nPostMouseEventButton
,
2215 pLOEvent
->m_nPostMouseEventModifier
);
2219 openDocumentInThread (gpointer data
)
2221 GTask
* task
= G_TASK(data
);
2222 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2223 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2225 std::lock_guard
<std::mutex
> aGuard(g_aLOKMutex
);
2226 if ( priv
->m_pDocument
)
2228 priv
->m_pDocument
->pClass
->destroy( priv
->m_pDocument
);
2229 priv
->m_pDocument
= nullptr;
2232 priv
->m_pOffice
->pClass
->registerCallback(priv
->m_pOffice
, globalCallbackWorker
, pDocView
);
2233 priv
->m_pDocument
= priv
->m_pOffice
->pClass
->documentLoad( priv
->m_pOffice
, priv
->m_aDocPath
);
2234 priv
->m_eDocumentType
= static_cast<LibreOfficeKitDocumentType
>(priv
->m_pDocument
->pClass
->getDocumentType(priv
->m_pDocument
));
2235 if ( !priv
->m_pDocument
)
2237 char *pError
= priv
->m_pOffice
->pClass
->getError( priv
->m_pOffice
);
2238 g_task_return_new_error(task
, g_quark_from_static_string ("LOK error"), 0, "%s", pError
);
2242 gdk_threads_add_idle(postDocumentLoad
, pDocView
);
2243 g_task_return_boolean (task
, true);
2248 setPartInThread(gpointer data
)
2250 GTask
* task
= G_TASK(data
);
2251 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2252 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2253 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2254 int nPart
= pLOEvent
->m_nPart
;
2256 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2257 std::stringstream ss
;
2258 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2259 g_info("%s", ss
.str().c_str());
2260 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2261 priv
->m_pDocument
->pClass
->setPart( priv
->m_pDocument
, nPart
);
2264 lok_doc_view_reset_view(pDocView
);
2268 setPartmodeInThread(gpointer data
)
2270 GTask
* task
= G_TASK(data
);
2271 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2272 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2273 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2274 int nPartMode
= pLOEvent
->m_nPartMode
;
2276 std::lock_guard
<std::mutex
> aGuard(g_aLOKMutex
);
2277 std::stringstream ss
;
2278 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2279 g_info("%s", ss
.str().c_str());
2280 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2281 priv
->m_pDocument
->pClass
->setPartMode( priv
->m_pDocument
, nPartMode
);
2285 setEditInThread(gpointer data
)
2287 GTask
* task
= G_TASK(data
);
2288 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2289 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2290 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2291 gboolean bWasEdit
= priv
->m_bEdit
;
2292 gboolean bEdit
= pLOEvent
->m_bEdit
;
2294 if (!priv
->m_bEdit
&& bEdit
)
2295 g_info("lok_doc_view_set_edit: entering edit mode");
2296 else if (priv
->m_bEdit
&& !bEdit
)
2298 g_info("lok_doc_view_set_edit: leaving edit mode");
2299 std::lock_guard
<std::mutex
> aGuard(g_aLOKMutex
);
2300 std::stringstream ss
;
2301 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2302 g_info("%s", ss
.str().c_str());
2303 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2304 priv
->m_pDocument
->pClass
->resetSelection(priv
->m_pDocument
);
2306 priv
->m_bEdit
= bEdit
;
2307 g_signal_emit(pDocView
, doc_view_signals
[EDIT_CHANGED
], 0, bWasEdit
);
2308 gdk_threads_add_idle(queueDraw
, GTK_WIDGET(pDocView
));
2312 postCommandInThread (gpointer data
)
2314 GTask
* task
= G_TASK(data
);
2315 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2316 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2317 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2319 std::lock_guard
<std::mutex
> aGuard(g_aLOKMutex
);
2320 std::stringstream ss
;
2321 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2322 g_info("%s", ss
.str().c_str());
2323 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2324 ss
.str(std::string());
2325 ss
<< "lok::Document::postUnoCommand(" << pLOEvent
->m_pCommand
<< ", " << pLOEvent
->m_pArguments
<< ")";
2326 g_info("%s", ss
.str().c_str());
2327 priv
->m_pDocument
->pClass
->postUnoCommand(priv
->m_pDocument
, pLOEvent
->m_pCommand
, pLOEvent
->m_pArguments
, pLOEvent
->m_bNotifyWhenFinished
);
2331 paintTileInThread (gpointer data
)
2333 GTask
* task
= G_TASK(data
);
2334 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2335 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2336 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2338 // check if "source" tile buffer is different from "current" tile buffer
2339 if (pLOEvent
->m_pTileBuffer
!= &*priv
->m_pTileBuffer
)
2341 pLOEvent
->m_pTileBuffer
= nullptr;
2342 g_task_return_new_error(task
,
2343 LOK_TILEBUFFER_ERROR
,
2344 LOK_TILEBUFFER_CHANGED
,
2345 "TileBuffer has changed");
2348 std::unique_ptr
<TileBuffer
>& buffer
= priv
->m_pTileBuffer
;
2349 int index
= pLOEvent
->m_nPaintTileX
* buffer
->m_nWidth
+ pLOEvent
->m_nPaintTileY
;
2350 if (buffer
->m_mTiles
.find(index
) != buffer
->m_mTiles
.end() &&
2351 buffer
->m_mTiles
[index
].valid
)
2354 cairo_surface_t
*pSurface
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32
, nTileSizePixels
, nTileSizePixels
);
2355 if (cairo_surface_status(pSurface
) != CAIRO_STATUS_SUCCESS
)
2357 cairo_surface_destroy(pSurface
);
2358 g_task_return_new_error(task
,
2359 LOK_TILEBUFFER_ERROR
,
2360 LOK_TILEBUFFER_MEMORY
,
2361 "Error allocating Surface");
2365 unsigned char* pBuffer
= cairo_image_surface_get_data(pSurface
);
2366 GdkRectangle aTileRectangle
;
2367 aTileRectangle
.x
= pixelToTwip(nTileSizePixels
, pLOEvent
->m_fPaintTileZoom
) * pLOEvent
->m_nPaintTileY
;
2368 aTileRectangle
.y
= pixelToTwip(nTileSizePixels
, pLOEvent
->m_fPaintTileZoom
) * pLOEvent
->m_nPaintTileX
;
2370 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2371 std::stringstream ss
;
2372 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2373 g_info("%s", ss
.str().c_str());
2374 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2375 ss
.str(std::string());
2376 GTimer
* aTimer
= g_timer_new();
2378 ss
<< "lok::Document::paintTile(" << static_cast<void*>(pBuffer
) << ", "
2379 << nTileSizePixels
<< ", " << nTileSizePixels
<< ", "
2380 << aTileRectangle
.x
<< ", " << aTileRectangle
.y
<< ", "
2381 << pixelToTwip(nTileSizePixels
, pLOEvent
->m_fPaintTileZoom
) << ", "
2382 << pixelToTwip(nTileSizePixels
, pLOEvent
->m_fPaintTileZoom
) << ")";
2384 priv
->m_pDocument
->pClass
->paintTile(priv
->m_pDocument
,
2386 nTileSizePixels
, nTileSizePixels
,
2387 aTileRectangle
.x
, aTileRectangle
.y
,
2388 pixelToTwip(nTileSizePixels
, pLOEvent
->m_fPaintTileZoom
),
2389 pixelToTwip(nTileSizePixels
, pLOEvent
->m_fPaintTileZoom
));
2392 g_timer_elapsed(aTimer
, &nElapsedMs
);
2393 ss
<< " rendered in " << (nElapsedMs
/ 1000.) << " milliseconds";
2394 g_info("%s", ss
.str().c_str());
2395 g_timer_destroy(aTimer
);
2397 cairo_surface_mark_dirty(pSurface
);
2399 // Its likely that while the tilebuffer has changed, one of the paint tile
2400 // requests has passed the previous check at start of this function, and has
2401 // rendered the tile already. We want to stop such rendered tiles from being
2402 // stored in new tile buffer.
2403 if (pLOEvent
->m_pTileBuffer
!= &*priv
->m_pTileBuffer
)
2405 pLOEvent
->m_pTileBuffer
= nullptr;
2406 g_task_return_new_error(task
,
2407 LOK_TILEBUFFER_ERROR
,
2408 LOK_TILEBUFFER_CHANGED
,
2409 "TileBuffer has changed");
2413 g_task_return_pointer(task
, pSurface
, reinterpret_cast<GDestroyNotify
>(cairo_surface_destroy
));
2418 lokThreadFunc(gpointer data
, gpointer
/*user_data*/)
2420 GTask
* task
= G_TASK(data
);
2421 LOEvent
* pLOEvent
= static_cast<LOEvent
*>(g_task_get_task_data(task
));
2422 LOKDocView
* pDocView
= LOK_DOC_VIEW(g_task_get_source_object(task
));
2423 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2425 switch (pLOEvent
->m_nType
)
2428 openDocumentInThread(task
);
2430 case LOK_POST_COMMAND
:
2431 postCommandInThread(task
);
2434 setEditInThread(task
);
2437 setPartInThread(task
);
2439 case LOK_SET_PARTMODE
:
2440 setPartmodeInThread(task
);
2443 // view-only/editable mode already checked during signal key signal emission
2444 postKeyEventInThread(task
);
2446 case LOK_PAINT_TILE
:
2447 paintTileInThread(task
);
2449 case LOK_POST_MOUSE_EVENT
:
2450 postMouseEventInThread(task
);
2452 case LOK_SET_GRAPHIC_SELECTION
:
2454 setGraphicSelectionInThread(task
);
2456 g_info ("LOK_SET_GRAPHIC_SELECTION: skipping graphical operation in view-only mode");
2458 case LOK_SET_CLIENT_ZOOM
:
2459 setClientZoomInThread(task
);
2463 g_object_unref(task
);
2466 static void lok_doc_view_init (LOKDocView
* pDocView
)
2468 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2469 priv
.m_pImpl
= new LOKDocViewPrivateImpl();
2471 gtk_widget_add_events(GTK_WIDGET(pDocView
),
2472 GDK_BUTTON_PRESS_MASK
2473 |GDK_BUTTON_RELEASE_MASK
2474 |GDK_BUTTON_MOTION_MASK
2476 |GDK_KEY_RELEASE_MASK
);
2478 priv
->lokThreadPool
= g_thread_pool_new(lokThreadFunc
,
2485 static void lok_doc_view_set_property (GObject
* object
, guint propId
, const GValue
*value
, GParamSpec
*pspec
)
2487 LOKDocView
* pDocView
= LOK_DOC_VIEW (object
);
2488 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2489 gboolean bDocPasswordEnabled
= priv
->m_nLOKFeatures
& LOK_FEATURE_DOCUMENT_PASSWORD
;
2490 gboolean bDocPasswordToModifyEnabled
= priv
->m_nLOKFeatures
& LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY
;
2491 gboolean bTiledAnnotationsEnabled
= !(priv
->m_nLOKFeatures
& LOK_FEATURE_NO_TILED_ANNOTATIONS
);
2496 priv
->m_aLOPath
= g_value_dup_string (value
);
2498 case PROP_LO_POINTER
:
2499 priv
->m_pOffice
= static_cast<LibreOfficeKit
*>(g_value_get_pointer(value
));
2501 case PROP_USER_PROFILE_URL
:
2502 priv
->m_pUserProfileURL
= g_value_dup_string(value
);
2505 priv
->m_aDocPath
= g_value_dup_string (value
);
2507 case PROP_DOC_POINTER
:
2508 priv
->m_pDocument
= static_cast<LibreOfficeKitDocument
*>(g_value_get_pointer(value
));
2509 priv
->m_eDocumentType
= static_cast<LibreOfficeKitDocumentType
>(priv
->m_pDocument
->pClass
->getDocumentType(priv
->m_pDocument
));
2512 lok_doc_view_set_edit (pDocView
, g_value_get_boolean (value
));
2515 lok_doc_view_set_zoom (pDocView
, g_value_get_float (value
));
2517 case PROP_DOC_WIDTH
:
2518 priv
->m_nDocumentWidthTwips
= g_value_get_long (value
);
2520 case PROP_DOC_HEIGHT
:
2521 priv
->m_nDocumentHeightTwips
= g_value_get_long (value
);
2523 case PROP_DOC_PASSWORD
:
2524 if (g_value_get_boolean (value
) != bDocPasswordEnabled
)
2526 priv
->m_nLOKFeatures
= priv
->m_nLOKFeatures
^ LOK_FEATURE_DOCUMENT_PASSWORD
;
2527 priv
->m_pOffice
->pClass
->setOptionalFeatures(priv
->m_pOffice
, priv
->m_nLOKFeatures
);
2530 case PROP_DOC_PASSWORD_TO_MODIFY
:
2531 if ( g_value_get_boolean (value
) != bDocPasswordToModifyEnabled
)
2533 priv
->m_nLOKFeatures
= priv
->m_nLOKFeatures
^ LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY
;
2534 priv
->m_pOffice
->pClass
->setOptionalFeatures(priv
->m_pOffice
, priv
->m_nLOKFeatures
);
2537 case PROP_TILED_ANNOTATIONS
:
2538 if ( g_value_get_boolean (value
) != bTiledAnnotationsEnabled
)
2540 priv
->m_nLOKFeatures
= priv
->m_nLOKFeatures
^ LOK_FEATURE_NO_TILED_ANNOTATIONS
;
2541 priv
->m_pOffice
->pClass
->setOptionalFeatures(priv
->m_pOffice
, priv
->m_nLOKFeatures
);
2545 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, propId
, pspec
);
2549 static void lok_doc_view_get_property (GObject
* object
, guint propId
, GValue
*value
, GParamSpec
*pspec
)
2551 LOKDocView
* pDocView
= LOK_DOC_VIEW (object
);
2552 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2557 g_value_set_string (value
, priv
->m_aLOPath
);
2559 case PROP_LO_POINTER
:
2560 g_value_set_pointer(value
, priv
->m_pOffice
);
2562 case PROP_USER_PROFILE_URL
:
2563 g_value_set_string(value
, priv
->m_pUserProfileURL
);
2566 g_value_set_string (value
, priv
->m_aDocPath
);
2568 case PROP_DOC_POINTER
:
2569 g_value_set_pointer(value
, priv
->m_pDocument
);
2572 g_value_set_boolean (value
, priv
->m_bEdit
);
2574 case PROP_LOAD_PROGRESS
:
2575 g_value_set_double (value
, priv
->m_nLoadProgress
);
2578 g_value_set_float (value
, priv
->m_fZoom
);
2580 case PROP_IS_LOADING
:
2581 g_value_set_boolean (value
, priv
->m_bIsLoading
);
2583 case PROP_DOC_WIDTH
:
2584 g_value_set_long (value
, priv
->m_nDocumentWidthTwips
);
2586 case PROP_DOC_HEIGHT
:
2587 g_value_set_long (value
, priv
->m_nDocumentHeightTwips
);
2589 case PROP_CAN_ZOOM_IN
:
2590 g_value_set_boolean (value
, priv
->m_bCanZoomIn
);
2592 case PROP_CAN_ZOOM_OUT
:
2593 g_value_set_boolean (value
, priv
->m_bCanZoomOut
);
2595 case PROP_DOC_PASSWORD
:
2596 g_value_set_boolean (value
, priv
->m_nLOKFeatures
& LOK_FEATURE_DOCUMENT_PASSWORD
);
2598 case PROP_DOC_PASSWORD_TO_MODIFY
:
2599 g_value_set_boolean (value
, priv
->m_nLOKFeatures
& LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY
);
2601 case PROP_TILED_ANNOTATIONS
:
2602 g_value_set_boolean (value
, !(priv
->m_nLOKFeatures
& LOK_FEATURE_NO_TILED_ANNOTATIONS
));
2605 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, propId
, pspec
);
2609 static gboolean
lok_doc_view_draw (GtkWidget
* pWidget
, cairo_t
* pCairo
)
2611 LOKDocView
*pDocView
= LOK_DOC_VIEW (pWidget
);
2613 renderDocument (pDocView
, pCairo
);
2614 renderOverlay (pDocView
, pCairo
);
2619 //rhbz#1444437 finalize may not occur immediately when this widget is destroyed
2620 //it may happen during GC of javascript, e.g. in gnome-documents but "destroy"
2621 //will be called promptly, so close documents in destroy, not finalize
2622 static void lok_doc_view_destroy (GtkWidget
* widget
)
2624 LOKDocView
* pDocView
= LOK_DOC_VIEW (widget
);
2625 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2627 // Ignore notifications sent to this view on shutdown.
2628 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
2629 std::stringstream ss
;
2630 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
2631 g_info("%s", ss
.str().c_str());
2632 if (priv
->m_pDocument
)
2634 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
2635 priv
->m_pDocument
->pClass
->registerCallback(priv
->m_pDocument
, nullptr, nullptr);
2638 if (priv
->lokThreadPool
)
2640 g_thread_pool_free(priv
->lokThreadPool
, true, true);
2641 priv
->lokThreadPool
= nullptr;
2646 if (priv
->m_pDocument
)
2648 if (priv
->m_pDocument
->pClass
->getViewsCount(priv
->m_pDocument
) > 1)
2650 priv
->m_pDocument
->pClass
->destroyView(priv
->m_pDocument
, priv
->m_nViewId
);
2654 if (priv
->m_pDocument
)
2656 priv
->m_pDocument
->pClass
->destroy (priv
->m_pDocument
);
2657 priv
->m_pDocument
= nullptr;
2659 if (priv
->m_pOffice
)
2661 priv
->m_pOffice
->pClass
->destroy (priv
->m_pOffice
);
2662 priv
->m_pOffice
= nullptr;
2667 GTK_WIDGET_CLASS (lok_doc_view_parent_class
)->destroy (widget
);
2670 static void lok_doc_view_finalize (GObject
* object
)
2672 LOKDocView
* pDocView
= LOK_DOC_VIEW (object
);
2673 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2675 delete priv
.m_pImpl
;
2676 priv
.m_pImpl
= nullptr;
2678 G_OBJECT_CLASS (lok_doc_view_parent_class
)->finalize (object
);
2681 static gboolean
lok_doc_view_initable_init (GInitable
*initable
, GCancellable
* /*cancellable*/, GError
**error
)
2683 LOKDocView
*pDocView
= LOK_DOC_VIEW (initable
);
2684 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
2686 if (priv
->m_pOffice
!= nullptr)
2689 priv
->m_pOffice
= lok_init_2(priv
->m_aLOPath
, priv
->m_pUserProfileURL
);
2691 if (priv
->m_pOffice
== nullptr)
2694 g_quark_from_static_string ("LOK initialization error"), 0,
2695 "Failed to get LibreOfficeKit context. Make sure path (%s) is correct",
2699 priv
->m_nLOKFeatures
|= LOK_FEATURE_PART_IN_INVALIDATION_CALLBACK
;
2700 priv
->m_pOffice
->pClass
->setOptionalFeatures(priv
->m_pOffice
, priv
->m_nLOKFeatures
);
2705 static void lok_doc_view_initable_iface_init (GInitableIface
*iface
)
2707 iface
->init
= lok_doc_view_initable_init
;
2710 static void lok_doc_view_class_init (LOKDocViewClass
* pClass
)
2712 GObjectClass
*pGObjectClass
= G_OBJECT_CLASS(pClass
);
2713 GtkWidgetClass
*pWidgetClass
= GTK_WIDGET_CLASS(pClass
);
2715 pGObjectClass
->get_property
= lok_doc_view_get_property
;
2716 pGObjectClass
->set_property
= lok_doc_view_set_property
;
2717 pGObjectClass
->finalize
= lok_doc_view_finalize
;
2719 pWidgetClass
->draw
= lok_doc_view_draw
;
2720 pWidgetClass
->button_press_event
= lok_doc_view_signal_button
;
2721 pWidgetClass
->button_release_event
= lok_doc_view_signal_button
;
2722 pWidgetClass
->key_press_event
= signalKey
;
2723 pWidgetClass
->key_release_event
= signalKey
;
2724 pWidgetClass
->motion_notify_event
= lok_doc_view_signal_motion
;
2725 pWidgetClass
->destroy
= lok_doc_view_destroy
;
2728 * LOKDocView:lopath:
2730 * The absolute path of the LibreOffice install.
2732 properties
[PROP_LO_PATH
] =
2733 g_param_spec_string("lopath",
2735 "LibreOffice Install Path",
2737 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2738 G_PARAM_CONSTRUCT_ONLY
|
2739 G_PARAM_STATIC_STRINGS
));
2742 * LOKDocView:lopointer:
2744 * A LibreOfficeKit* in case lok_init() is already called
2747 properties
[PROP_LO_POINTER
] =
2748 g_param_spec_pointer("lopointer",
2750 "A LibreOfficeKit* from lok_init()",
2751 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2752 G_PARAM_CONSTRUCT_ONLY
|
2753 G_PARAM_STATIC_STRINGS
));
2756 * LOKDocView:userprofileurl:
2758 * The absolute path of the LibreOffice user profile.
2760 properties
[PROP_USER_PROFILE_URL
] =
2761 g_param_spec_string("userprofileurl",
2762 "User profile path",
2763 "LibreOffice user profile path",
2765 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2766 G_PARAM_CONSTRUCT_ONLY
|
2767 G_PARAM_STATIC_STRINGS
));
2770 * LOKDocView:docpath:
2772 * The path of the document that is currently being viewed.
2774 properties
[PROP_DOC_PATH
] =
2775 g_param_spec_string("docpath",
2777 "The URI of the document to open",
2779 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2780 G_PARAM_STATIC_STRINGS
));
2783 * LOKDocView:docpointer:
2785 * A LibreOfficeKitDocument* in case documentLoad() is already called
2788 properties
[PROP_DOC_POINTER
] =
2789 g_param_spec_pointer("docpointer",
2791 "A LibreOfficeKitDocument* from documentLoad()",
2792 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2793 G_PARAM_STATIC_STRINGS
));
2796 * LOKDocView:editable:
2798 * Whether the document loaded inside of #LOKDocView is editable or not.
2800 properties
[PROP_EDITABLE
] =
2801 g_param_spec_boolean("editable",
2803 "Whether the content is in edit mode or not",
2805 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2806 G_PARAM_STATIC_STRINGS
));
2809 * LOKDocView:load-progress:
2811 * The percent completion of the current loading operation of the
2812 * document. This can be used for progress bars. Note that this is not a
2813 * very accurate progress indicator, and its value might reset it couple of
2814 * times to 0 and start again. You should not rely on its numbers.
2816 properties
[PROP_LOAD_PROGRESS
] =
2817 g_param_spec_double("load-progress",
2818 "Estimated Load Progress",
2819 "Shows the progress of the document load operation",
2821 static_cast<GParamFlags
>(G_PARAM_READABLE
|
2822 G_PARAM_STATIC_STRINGS
));
2825 * LOKDocView:zoom-level:
2827 * The current zoom level of the document loaded inside #LOKDocView. The
2828 * default value is 1.0.
2830 properties
[PROP_ZOOM
] =
2831 g_param_spec_float("zoom-level",
2833 "The current zoom level of the content",
2835 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2836 G_PARAM_STATIC_STRINGS
));
2839 * LOKDocView:is-loading:
2841 * Whether the requested document is being loaded or not. %TRUE if it is
2842 * being loaded, otherwise %FALSE.
2844 properties
[PROP_IS_LOADING
] =
2845 g_param_spec_boolean("is-loading",
2847 "Whether the view is loading a document",
2849 static_cast<GParamFlags
>(G_PARAM_READABLE
|
2850 G_PARAM_STATIC_STRINGS
));
2853 * LOKDocView:doc-width:
2855 * The width of the currently loaded document in #LOKDocView in twips.
2857 properties
[PROP_DOC_WIDTH
] =
2858 g_param_spec_long("doc-width",
2860 "Width of the document in twips",
2862 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2863 G_PARAM_STATIC_STRINGS
));
2866 * LOKDocView:doc-height:
2868 * The height of the currently loaded document in #LOKDocView in twips.
2870 properties
[PROP_DOC_HEIGHT
] =
2871 g_param_spec_long("doc-height",
2873 "Height of the document in twips",
2875 static_cast<GParamFlags
>(G_PARAM_READWRITE
|
2876 G_PARAM_STATIC_STRINGS
));
2879 * LOKDocView:can-zoom-in:
2881 * It tells whether the view can further be zoomed in or not.
2883 properties
[PROP_CAN_ZOOM_IN
] =
2884 g_param_spec_boolean("can-zoom-in",
2886 "Whether the view can be zoomed in further",
2888 static_cast<GParamFlags
>(G_PARAM_READABLE
2889 | G_PARAM_STATIC_STRINGS
));
2892 * LOKDocView:can-zoom-out:
2894 * It tells whether the view can further be zoomed out or not.
2896 properties
[PROP_CAN_ZOOM_OUT
] =
2897 g_param_spec_boolean("can-zoom-out",
2899 "Whether the view can be zoomed out further",
2901 static_cast<GParamFlags
>(G_PARAM_READABLE
2902 | G_PARAM_STATIC_STRINGS
));
2905 * LOKDocView:doc-password:
2907 * Set it to true if client supports providing password for viewing
2908 * password protected documents
2910 properties
[PROP_DOC_PASSWORD
] =
2911 g_param_spec_boolean("doc-password",
2912 "Document password capability",
2913 "Whether client supports providing document passwords",
2915 static_cast<GParamFlags
>(G_PARAM_READWRITE
2916 | G_PARAM_STATIC_STRINGS
));
2919 * LOKDocView:doc-password-to-modify:
2921 * Set it to true if client supports providing password for edit-protected documents
2923 properties
[PROP_DOC_PASSWORD_TO_MODIFY
] =
2924 g_param_spec_boolean("doc-password-to-modify",
2925 "Edit document password capability",
2926 "Whether the client supports providing passwords to edit documents",
2928 static_cast<GParamFlags
>(G_PARAM_READWRITE
2929 | G_PARAM_STATIC_STRINGS
));
2932 * LOKDocView:tiled-annotations-rendering:
2934 * Set it to false if client does not want LO to render comments in tiles and
2935 * instead interested in using comments API to access comments
2937 properties
[PROP_TILED_ANNOTATIONS
] =
2938 g_param_spec_boolean("tiled-annotations",
2939 "Render comments in tiles",
2940 "Whether the client wants in tile comment rendering",
2942 static_cast<GParamFlags
>(G_PARAM_READWRITE
2943 | G_PARAM_STATIC_STRINGS
));
2945 g_object_class_install_properties(pGObjectClass
, PROP_LAST
, properties
);
2948 * LOKDocView::load-changed:
2949 * @pDocView: the #LOKDocView on which the signal is emitted
2950 * @fLoadProgress: the new progress value
2952 doc_view_signals
[LOAD_CHANGED
] =
2953 g_signal_new("load-changed",
2954 G_TYPE_FROM_CLASS (pGObjectClass
),
2958 g_cclosure_marshal_VOID__DOUBLE
,
2963 * LOKDocView::edit-changed:
2964 * @pDocView: the #LOKDocView on which the signal is emitted
2965 * @bEdit: the new edit value of the view
2967 doc_view_signals
[EDIT_CHANGED
] =
2968 g_signal_new("edit-changed",
2969 G_TYPE_FROM_CLASS (pGObjectClass
),
2973 g_cclosure_marshal_VOID__BOOLEAN
,
2978 * LOKDocView::command-changed:
2979 * @pDocView: the #LOKDocView on which the signal is emitted
2980 * @aCommand: the command that was changed
2982 doc_view_signals
[COMMAND_CHANGED
] =
2983 g_signal_new("command-changed",
2984 G_TYPE_FROM_CLASS(pGObjectClass
),
2988 g_cclosure_marshal_VOID__STRING
,
2993 * LOKDocView::search-not-found:
2994 * @pDocView: the #LOKDocView on which the signal is emitted
2995 * @aCommand: the string for which the search was not found.
2997 doc_view_signals
[SEARCH_NOT_FOUND
] =
2998 g_signal_new("search-not-found",
2999 G_TYPE_FROM_CLASS(pGObjectClass
),
3003 g_cclosure_marshal_VOID__STRING
,
3008 * LOKDocView::part-changed:
3009 * @pDocView: the #LOKDocView on which the signal is emitted
3010 * @aCommand: the part number which the view changed to
3012 doc_view_signals
[PART_CHANGED
] =
3013 g_signal_new("part-changed",
3014 G_TYPE_FROM_CLASS(pGObjectClass
),
3018 g_cclosure_marshal_VOID__INT
,
3023 * LOKDocView::size-changed:
3024 * @pDocView: the #LOKDocView on which the signal is emitted
3025 * @aCommand: NULL, we just notify that want to notify the UI elements that are interested.
3027 doc_view_signals
[SIZE_CHANGED
] =
3028 g_signal_new("size-changed",
3029 G_TYPE_FROM_CLASS(pGObjectClass
),
3033 g_cclosure_marshal_VOID__VOID
,
3038 * LOKDocView::hyperlinked-clicked:
3039 * @pDocView: the #LOKDocView on which the signal is emitted
3040 * @aHyperlink: the URI which the application should handle
3042 doc_view_signals
[HYPERLINK_CLICKED
] =
3043 g_signal_new("hyperlink-clicked",
3044 G_TYPE_FROM_CLASS(pGObjectClass
),
3048 g_cclosure_marshal_VOID__STRING
,
3053 * LOKDocView::cursor-changed:
3054 * @pDocView: the #LOKDocView on which the signal is emitted
3055 * @nX: The new cursor position (X coordinate) in pixels
3056 * @nY: The new cursor position (Y coordinate) in pixels
3057 * @nWidth: The width of new cursor
3058 * @nHeight: The height of new cursor
3060 doc_view_signals
[CURSOR_CHANGED
] =
3061 g_signal_new("cursor-changed",
3062 G_TYPE_FROM_CLASS(pGObjectClass
),
3066 g_cclosure_marshal_generic
,
3068 G_TYPE_INT
, G_TYPE_INT
,
3069 G_TYPE_INT
, G_TYPE_INT
);
3072 * LOKDocView::search-result-count:
3073 * @pDocView: the #LOKDocView on which the signal is emitted
3074 * @aCommand: number of matches.
3076 doc_view_signals
[SEARCH_RESULT_COUNT
] =
3077 g_signal_new("search-result-count",
3078 G_TYPE_FROM_CLASS(pGObjectClass
),
3082 g_cclosure_marshal_VOID__STRING
,
3087 * LOKDocView::command-result:
3088 * @pDocView: the #LOKDocView on which the signal is emitted
3089 * @aCommand: JSON containing the info about the command that finished,
3090 * and its success status.
3092 doc_view_signals
[COMMAND_RESULT
] =
3093 g_signal_new("command-result",
3094 G_TYPE_FROM_CLASS(pGObjectClass
),
3098 g_cclosure_marshal_VOID__STRING
,
3103 * LOKDocView::address-changed:
3104 * @pDocView: the #LOKDocView on which the signal is emitted
3105 * @aCommand: formula text content
3107 doc_view_signals
[ADDRESS_CHANGED
] =
3108 g_signal_new("address-changed",
3109 G_TYPE_FROM_CLASS(pGObjectClass
),
3113 g_cclosure_marshal_VOID__STRING
,
3118 * LOKDocView::formula-changed:
3119 * @pDocView: the #LOKDocView on which the signal is emitted
3120 * @aCommand: formula text content
3122 doc_view_signals
[FORMULA_CHANGED
] =
3123 g_signal_new("formula-changed",
3124 G_TYPE_FROM_CLASS(pGObjectClass
),
3128 g_cclosure_marshal_VOID__STRING
,
3133 * LOKDocView::text-selection:
3134 * @pDocView: the #LOKDocView on which the signal is emitted
3135 * @bIsTextSelected: whether text selected is non-null
3137 doc_view_signals
[TEXT_SELECTION
] =
3138 g_signal_new("text-selection",
3139 G_TYPE_FROM_CLASS(pGObjectClass
),
3143 g_cclosure_marshal_VOID__BOOLEAN
,
3148 * LOKDocView::password-required:
3149 * @pDocView: the #LOKDocView on which the signal is emitted
3150 * @pUrl: URL of the document for which password is required
3151 * @bModify: whether password id required to modify the document
3152 * This is true when password is required to edit the document,
3153 * while it can still be viewed without password. In such cases, provide a NULL
3154 * password for read-only access to the document.
3155 * If false, password is required for opening the document, and document
3156 * cannot be opened without providing a valid password.
3158 * Password must be provided by calling lok_doc_view_set_document_password
3159 * function with pUrl as provided by the callback.
3161 * Upon entering a invalid password, another `password-required` signal is
3163 * Upon entering a valid password, document starts to load.
3164 * Upon entering a NULL password: if bModify is %TRUE, document starts to
3165 * open in view-only mode, else loading of document is aborted.
3167 doc_view_signals
[PASSWORD_REQUIRED
] =
3168 g_signal_new("password-required",
3169 G_TYPE_FROM_CLASS(pGObjectClass
),
3173 g_cclosure_marshal_generic
,
3179 * LOKDocView::comment:
3180 * @pDocView: the #LOKDocView on which the signal is emitted
3181 * @pComment: the JSON string containing comment notification
3182 * The has following structure containing the information telling whether
3183 * the comment has been added, deleted or modified.
3190 * "author": "Unknown Author",
3191 * "text": "This is a comment",
3192 * "dateTime": "2016-08-18T13:13:00",
3193 * "anchorPos": "4529, 3906",
3194 * "textRange": "1418, 3906, 3111, 919"
3197 * 'action' can be 'Add', 'Remove' or 'Modify' depending on whether
3198 * comment has been added, removed or modified.
3199 * 'parent' is a non-zero comment id if this comment is a reply comment,
3200 * otherwise its a root comment.
3202 doc_view_signals
[COMMENT
] =
3203 g_signal_new("comment",
3204 G_TYPE_FROM_CLASS(pGObjectClass
),
3208 g_cclosure_marshal_generic
,
3213 SAL_DLLPUBLIC_EXPORT GtkWidget
*
3214 lok_doc_view_new (const gchar
* pPath
, GCancellable
*cancellable
, GError
**error
)
3216 return GTK_WIDGET (g_initable_new (LOK_TYPE_DOC_VIEW
, cancellable
, error
,
3217 "lopath", pPath
== nullptr ? LOK_PATH
: pPath
,
3218 "halign", GTK_ALIGN_CENTER
,
3219 "valign", GTK_ALIGN_CENTER
,
3223 SAL_DLLPUBLIC_EXPORT GtkWidget
*
3224 lok_doc_view_new_from_user_profile (const gchar
* pPath
, const gchar
* pUserProfile
, GCancellable
*cancellable
, GError
**error
)
3226 return GTK_WIDGET(g_initable_new(LOK_TYPE_DOC_VIEW
, cancellable
, error
,
3227 "lopath", pPath
== nullptr ? LOK_PATH
: pPath
,
3228 "userprofileurl", pUserProfile
,
3229 "halign", GTK_ALIGN_CENTER
,
3230 "valign", GTK_ALIGN_CENTER
,
3234 SAL_DLLPUBLIC_EXPORT GtkWidget
* lok_doc_view_new_from_widget(LOKDocView
* pOldLOKDocView
,
3235 const gchar
* pRenderingArguments
)
3237 LOKDocViewPrivate
& pOldPriv
= getPrivate(pOldLOKDocView
);
3238 GtkWidget
* pNewDocView
= GTK_WIDGET(g_initable_new(LOK_TYPE_DOC_VIEW
, /*cancellable=*/nullptr, /*error=*/nullptr,
3239 "lopath", pOldPriv
->m_aLOPath
,
3240 "userprofileurl", pOldPriv
->m_pUserProfileURL
,
3241 "lopointer", pOldPriv
->m_pOffice
,
3242 "docpointer", pOldPriv
->m_pDocument
,
3243 "halign", GTK_ALIGN_CENTER
,
3244 "valign", GTK_ALIGN_CENTER
,
3247 // No documentLoad(), just a createView().
3248 LibreOfficeKitDocument
* pDocument
= lok_doc_view_get_document(LOK_DOC_VIEW(pNewDocView
));
3249 LOKDocViewPrivate
& pNewPriv
= getPrivate(LOK_DOC_VIEW(pNewDocView
));
3250 // Store the view id only later in postDocumentLoad(), as
3251 // initializeForRendering() changes the id in Impress.
3252 pDocument
->pClass
->createView(pDocument
);
3253 pNewPriv
->m_aRenderingArguments
= pRenderingArguments
;
3255 postDocumentLoad(pNewDocView
);
3259 SAL_DLLPUBLIC_EXPORT gboolean
3260 lok_doc_view_open_document_finish (LOKDocView
* pDocView
, GAsyncResult
* res
, GError
** error
)
3262 GTask
* task
= G_TASK(res
);
3264 g_return_val_if_fail(g_task_is_valid(res
, pDocView
), false);
3265 g_return_val_if_fail(g_task_get_source_tag(task
) == lok_doc_view_open_document
, false);
3266 g_return_val_if_fail(error
== nullptr || *error
== nullptr, false);
3268 return g_task_propagate_boolean(task
, error
);
3271 SAL_DLLPUBLIC_EXPORT
void
3272 lok_doc_view_open_document (LOKDocView
* pDocView
,
3274 const gchar
* pRenderingArguments
,
3275 GCancellable
* cancellable
,
3276 GAsyncReadyCallback callback
,
3279 GTask
* task
= g_task_new(pDocView
, cancellable
, callback
, userdata
);
3280 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3281 GError
* error
= nullptr;
3283 LOEvent
* pLOEvent
= new LOEvent(LOK_LOAD_DOC
);
3284 pLOEvent
->m_pPath
= pPath
;
3286 priv
->m_aDocPath
= pPath
;
3287 if (pRenderingArguments
)
3288 priv
->m_aRenderingArguments
= pRenderingArguments
;
3289 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
3290 g_task_set_source_tag(task
, reinterpret_cast<gpointer
>(lok_doc_view_open_document
));
3292 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
3293 if (error
!= nullptr)
3295 g_warning("Unable to call LOK_LOAD_DOC: %s", error
->message
);
3296 g_clear_error(&error
);
3298 g_object_unref(task
);
3301 SAL_DLLPUBLIC_EXPORT LibreOfficeKitDocument
*
3302 lok_doc_view_get_document (LOKDocView
* pDocView
)
3304 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3305 return priv
->m_pDocument
;
3308 SAL_DLLPUBLIC_EXPORT
void
3309 lok_doc_view_set_visible_area (LOKDocView
* pDocView
, GdkRectangle
* pVisibleArea
)
3314 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3315 priv
->m_aVisibleArea
= *pVisibleArea
;
3316 priv
->m_bVisibleAreaSet
= true;
3320 // This used to be rtl::math::approxEqual() but since that isn't inline anymore
3321 // in rtl/math.hxx and was moved into libuno_sal as rtl_math_approxEqual() to
3322 // cater for representable integer cases and we don't want to link against
3323 // libuno_sal, we'll have to have an own implementation. The special large
3324 // integer cases seems not be needed here.
3325 inline bool lok_approxEqual(double a
, double b
)
3327 static const double e48
= 1.0 / (16777216.0 * 16777216.0);
3330 if (a
== 0.0 || b
== 0.0)
3332 const double d
= fabs(a
- b
);
3333 return (d
< fabs(a
) * e48
&& d
< fabs(b
) * e48
);
3337 SAL_DLLPUBLIC_EXPORT
void
3338 lok_doc_view_set_zoom (LOKDocView
* pDocView
, float fZoom
)
3340 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3341 GError
* error
= nullptr;
3343 if (!priv
->m_pDocument
)
3346 // Clamp the input value in [MIN_ZOOM, MAX_ZOOM]
3347 fZoom
= fZoom
< MIN_ZOOM
? MIN_ZOOM
: fZoom
;
3348 fZoom
= fZoom
> MAX_ZOOM
? MAX_ZOOM
: fZoom
;
3350 if (lok_approxEqual(fZoom
, priv
->m_fZoom
))
3353 priv
->m_fZoom
= fZoom
;
3354 long nDocumentWidthPixels
= twipToPixel(priv
->m_nDocumentWidthTwips
, fZoom
);
3355 long nDocumentHeightPixels
= twipToPixel(priv
->m_nDocumentHeightTwips
, fZoom
);
3356 // Total number of columns in this document.
3357 guint nColumns
= ceil((double)nDocumentWidthPixels
/ nTileSizePixels
);
3359 priv
->m_pTileBuffer
= std::unique_ptr
<TileBuffer
>(new TileBuffer(nColumns
));
3360 gtk_widget_set_size_request(GTK_WIDGET(pDocView
),
3361 nDocumentWidthPixels
,
3362 nDocumentHeightPixels
);
3364 g_object_notify_by_pspec(G_OBJECT(pDocView
), properties
[PROP_ZOOM
]);
3366 // set properties to indicate if view can be further zoomed in/out
3367 bool bCanZoomIn
= priv
->m_fZoom
< MAX_ZOOM
;
3368 bool bCanZoomOut
= priv
->m_fZoom
> MIN_ZOOM
;
3369 if (bCanZoomIn
!= bool(priv
->m_bCanZoomIn
))
3371 priv
->m_bCanZoomIn
= bCanZoomIn
;
3372 g_object_notify_by_pspec(G_OBJECT(pDocView
), properties
[PROP_CAN_ZOOM_IN
]);
3374 if (bCanZoomOut
!= bool(priv
->m_bCanZoomOut
))
3376 priv
->m_bCanZoomOut
= bCanZoomOut
;
3377 g_object_notify_by_pspec(G_OBJECT(pDocView
), properties
[PROP_CAN_ZOOM_OUT
]);
3380 // Update the client's view size
3381 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
3382 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_CLIENT_ZOOM
);
3383 pLOEvent
->m_nTilePixelWidth
= nTileSizePixels
;
3384 pLOEvent
->m_nTilePixelHeight
= nTileSizePixels
;
3385 pLOEvent
->m_nTileTwipWidth
= pixelToTwip(nTileSizePixels
, fZoom
);
3386 pLOEvent
->m_nTileTwipHeight
= pixelToTwip(nTileSizePixels
, fZoom
);
3387 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
3389 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
3390 if (error
!= nullptr)
3392 g_warning("Unable to call LOK_SET_CLIENT_ZOOM: %s", error
->message
);
3393 g_clear_error(&error
);
3395 g_object_unref(task
);
3397 priv
->m_nTileSizeTwips
= pixelToTwip(nTileSizePixels
, priv
->m_fZoom
);
3400 SAL_DLLPUBLIC_EXPORT gfloat
3401 lok_doc_view_get_zoom (LOKDocView
* pDocView
)
3403 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3404 return priv
->m_fZoom
;
3407 SAL_DLLPUBLIC_EXPORT gint
3408 lok_doc_view_get_parts (LOKDocView
* pDocView
)
3410 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3411 if (!priv
->m_pDocument
)
3414 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
3415 std::stringstream ss
;
3416 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
3417 g_info("%s", ss
.str().c_str());
3418 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
3419 return priv
->m_pDocument
->pClass
->getParts( priv
->m_pDocument
);
3422 SAL_DLLPUBLIC_EXPORT gint
3423 lok_doc_view_get_part (LOKDocView
* pDocView
)
3425 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3426 if (!priv
->m_pDocument
)
3429 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
3430 std::stringstream ss
;
3431 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
3432 g_info("%s", ss
.str().c_str());
3433 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
3434 return priv
->m_pDocument
->pClass
->getPart( priv
->m_pDocument
);
3437 SAL_DLLPUBLIC_EXPORT
void
3438 lok_doc_view_set_part (LOKDocView
* pDocView
, int nPart
)
3440 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3441 if (!priv
->m_pDocument
)
3444 if (nPart
< 0 || nPart
>= priv
->m_nParts
)
3446 g_warning("Invalid part request : %d", nPart
);
3450 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
3451 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_PART
);
3452 GError
* error
= nullptr;
3454 pLOEvent
->m_nPart
= nPart
;
3455 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
3457 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
3458 if (error
!= nullptr)
3460 g_warning("Unable to call LOK_SET_PART: %s", error
->message
);
3461 g_clear_error(&error
);
3463 g_object_unref(task
);
3464 priv
->m_nPartId
= nPart
;
3467 SAL_DLLPUBLIC_EXPORT gchar
*
3468 lok_doc_view_get_part_name (LOKDocView
* pDocView
, int nPart
)
3470 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3471 if (!priv
->m_pDocument
)
3474 std::unique_lock
<std::mutex
> aGuard(g_aLOKMutex
);
3475 std::stringstream ss
;
3476 ss
<< "lok::Document::setView(" << priv
->m_nViewId
<< ")";
3477 g_info("%s", ss
.str().c_str());
3478 priv
->m_pDocument
->pClass
->setView(priv
->m_pDocument
, priv
->m_nViewId
);
3479 return priv
->m_pDocument
->pClass
->getPartName( priv
->m_pDocument
, nPart
);
3482 SAL_DLLPUBLIC_EXPORT
void
3483 lok_doc_view_set_partmode(LOKDocView
* pDocView
,
3486 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3487 if (!priv
->m_pDocument
)
3490 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
3491 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_PARTMODE
);
3492 GError
* error
= nullptr;
3494 pLOEvent
->m_nPartMode
= nPartMode
;
3495 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
3497 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
3498 if (error
!= nullptr)
3500 g_warning("Unable to call LOK_SET_PARTMODE: %s", error
->message
);
3501 g_clear_error(&error
);
3503 g_object_unref(task
);
3506 SAL_DLLPUBLIC_EXPORT
void
3507 lok_doc_view_reset_view(LOKDocView
* pDocView
)
3509 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3511 if (priv
->m_pTileBuffer
!= nullptr)
3512 priv
->m_pTileBuffer
->resetAllTiles();
3513 priv
->m_nLoadProgress
= 0.0;
3515 memset(&priv
->m_aVisibleCursor
, 0, sizeof(priv
->m_aVisibleCursor
));
3516 priv
->m_bCursorOverlayVisible
= false;
3517 priv
->m_bCursorVisible
= false;
3519 priv
->m_nLastButtonPressTime
= 0;
3520 priv
->m_nLastButtonReleaseTime
= 0;
3521 priv
->m_aTextSelectionRectangles
.clear();
3523 memset(&priv
->m_aTextSelectionStart
, 0, sizeof(priv
->m_aTextSelectionStart
));
3524 memset(&priv
->m_aTextSelectionEnd
, 0, sizeof(priv
->m_aTextSelectionEnd
));
3525 memset(&priv
->m_aGraphicSelection
, 0, sizeof(priv
->m_aGraphicSelection
));
3526 priv
->m_bInDragGraphicSelection
= false;
3527 memset(&priv
->m_aCellCursor
, 0, sizeof(priv
->m_aCellCursor
));
3529 cairo_surface_destroy(priv
->m_pHandleStart
);
3530 priv
->m_pHandleStart
= nullptr;
3531 memset(&priv
->m_aHandleStartRect
, 0, sizeof(priv
->m_aHandleStartRect
));
3532 priv
->m_bInDragStartHandle
= false;
3534 cairo_surface_destroy(priv
->m_pHandleMiddle
);
3535 priv
->m_pHandleMiddle
= nullptr;
3536 memset(&priv
->m_aHandleMiddleRect
, 0, sizeof(priv
->m_aHandleMiddleRect
));
3537 priv
->m_bInDragMiddleHandle
= false;
3539 cairo_surface_destroy(priv
->m_pHandleEnd
);
3540 priv
->m_pHandleEnd
= nullptr;
3541 memset(&priv
->m_aHandleEndRect
, 0, sizeof(priv
->m_aHandleEndRect
));
3542 priv
->m_bInDragEndHandle
= false;
3544 memset(&priv
->m_aGraphicHandleRects
, 0, sizeof(priv
->m_aGraphicHandleRects
));
3545 memset(&priv
->m_bInDragGraphicHandles
, 0, sizeof(priv
->m_bInDragGraphicHandles
));
3547 gtk_widget_queue_draw(GTK_WIDGET(pDocView
));
3550 SAL_DLLPUBLIC_EXPORT
void
3551 lok_doc_view_set_edit(LOKDocView
* pDocView
,
3554 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3555 if (!priv
->m_pDocument
)
3558 GTask
* task
= g_task_new(pDocView
, nullptr, nullptr, nullptr);
3559 LOEvent
* pLOEvent
= new LOEvent(LOK_SET_EDIT
);
3560 GError
* error
= nullptr;
3562 pLOEvent
->m_bEdit
= bEdit
;
3563 g_task_set_task_data(task
, pLOEvent
, LOEvent::destroy
);
3565 g_thread_pool_push(priv
->lokThreadPool
, g_object_ref(task
), &error
);
3566 if (error
!= nullptr)
3568 g_warning("Unable to call LOK_SET_EDIT: %s", error
->message
);
3569 g_clear_error(&error
);
3571 g_object_unref(task
);
3574 SAL_DLLPUBLIC_EXPORT gboolean
3575 lok_doc_view_get_edit (LOKDocView
* pDocView
)
3577 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3578 return priv
->m_bEdit
;
3581 SAL_DLLPUBLIC_EXPORT
void
3582 lok_doc_view_post_command (LOKDocView
* pDocView
,
3583 const gchar
* pCommand
,
3584 const gchar
* pArguments
,
3585 gboolean bNotifyWhenFinished
)
3587 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3588 if (!priv
->m_pDocument
)
3592 LOKPostCommand(pDocView
, pCommand
, pArguments
, bNotifyWhenFinished
);
3594 g_info ("LOK_POST_COMMAND: ignoring commands in view-only mode");
3597 SAL_DLLPUBLIC_EXPORT
void
3598 lok_doc_view_find_prev (LOKDocView
* pDocView
,
3600 gboolean bHighlightAll
)
3602 doSearch(pDocView
, pText
, true, bHighlightAll
);
3605 SAL_DLLPUBLIC_EXPORT
void
3606 lok_doc_view_find_next (LOKDocView
* pDocView
,
3608 gboolean bHighlightAll
)
3610 doSearch(pDocView
, pText
, false, bHighlightAll
);
3613 SAL_DLLPUBLIC_EXPORT
void
3614 lok_doc_view_highlight_all (LOKDocView
* pDocView
,
3617 doSearch(pDocView
, pText
, false, true);
3620 SAL_DLLPUBLIC_EXPORT gchar
*
3621 lok_doc_view_copy_selection (LOKDocView
* pDocView
,
3622 const gchar
* pMimeType
,
3623 gchar
** pUsedMimeType
)
3625 LibreOfficeKitDocument
* pDocument
= lok_doc_view_get_document(pDocView
);
3629 std::stringstream ss
;
3630 ss
<< "lok::Document::getTextSelection('" << pMimeType
<< "')";
3631 g_info("%s", ss
.str().c_str());
3632 return pDocument
->pClass
->getTextSelection(pDocument
, pMimeType
, pUsedMimeType
);
3635 SAL_DLLPUBLIC_EXPORT gboolean
3636 lok_doc_view_paste (LOKDocView
* pDocView
,
3637 const gchar
* pMimeType
,
3641 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3642 LibreOfficeKitDocument
* pDocument
= priv
->m_pDocument
;
3650 g_info ("ignoring paste in view-only mode");
3656 std::stringstream ss
;
3657 ss
<< "lok::Document::paste('" << pMimeType
<< "', '" << std::string(pData
, nSize
) << ", "<<nSize
<<"')";
3658 g_info("%s", ss
.str().c_str());
3659 ret
= pDocument
->pClass
->paste(pDocument
, pMimeType
, pData
, nSize
);
3665 SAL_DLLPUBLIC_EXPORT
void
3666 lok_doc_view_set_document_password (LOKDocView
* pDocView
,
3668 const gchar
* pPassword
)
3670 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3672 priv
->m_pOffice
->pClass
->setDocumentPassword(priv
->m_pOffice
, pURL
, pPassword
);
3675 SAL_DLLPUBLIC_EXPORT gchar
*
3676 lok_doc_view_get_version_info (LOKDocView
* pDocView
)
3678 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3680 return priv
->m_pOffice
->pClass
->getVersionInfo(priv
->m_pOffice
);
3684 SAL_DLLPUBLIC_EXPORT gfloat
3685 lok_doc_view_pixel_to_twip (LOKDocView
* pDocView
, float fInput
)
3687 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3688 return pixelToTwip(fInput
, priv
->m_fZoom
);
3691 SAL_DLLPUBLIC_EXPORT gfloat
3692 lok_doc_view_twip_to_pixel (LOKDocView
* pDocView
, float fInput
)
3694 LOKDocViewPrivate
& priv
= getPrivate(pDocView
);
3695 return twipToPixel(fInput
, priv
->m_fZoom
);
3698 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */