LibreOffice Module libreofficekit (master) 1
lokdocview.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 */
9
10#include <sal/types.h>
11#include <math.h>
12#include <string.h>
13#include <memory>
14#include <utility>
15#include <vector>
16#include <string>
17#include <sstream>
18#include <mutex>
19#include <boost/property_tree/json_parser.hpp>
20
21#include <com/sun/star/awt/Key.hpp>
22#include <LibreOfficeKit/LibreOfficeKit.h>
23#include <LibreOfficeKit/LibreOfficeKitInit.h>
24#include <LibreOfficeKit/LibreOfficeKitEnums.h>
25#include <LibreOfficeKit/LibreOfficeKitGtk.h>
26#include <vcl/event.hxx>
27
28#include "tilebuffer.hxx"
29
30#if !GLIB_CHECK_VERSION(2,32,0)
31#define G_SOURCE_REMOVE FALSE
32#define G_SOURCE_CONTINUE TRUE
33#endif
34#if !GLIB_CHECK_VERSION(2,40,0)
35#define g_info(...) g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, __VA_ARGS__)
36#endif
37
38// Cursor bitmaps from the installation set.
39#define CURSOR_HANDLE_DIR "/../share/libreofficekit/"
40// Number of handles around a graphic selection.
41#define GRAPHIC_HANDLE_COUNT 8
42// Maximum Zoom allowed
43#define MAX_ZOOM 5.0f
44// Minimum Zoom allowed
45#define MIN_ZOOM 0.25f
46
48static std::mutex g_aLOKMutex;
49
50namespace {
51
53struct ViewRectangle
54{
55 int m_nPart;
56 GdkRectangle m_aRectangle;
57
58 ViewRectangle(int nPart = 0, const GdkRectangle& rRectangle = GdkRectangle())
59 : m_nPart(nPart),
60 m_aRectangle(rRectangle)
61 {
62 }
63};
64
66struct ViewRectangles
67{
68 int m_nPart;
69 std::vector<GdkRectangle> m_aRectangles;
70
71 ViewRectangles(int nPart = 0, std::vector<GdkRectangle>&& rRectangles = std::vector<GdkRectangle>())
72 : m_nPart(nPart),
73 m_aRectangles(std::move(rRectangles))
74 {
75 }
76};
77
79struct LOKDocViewPrivateImpl
80{
81 std::string m_aLOPath;
82 std::string m_aUserProfileURL;
83 std::string m_aDocPath;
84 std::string m_aRenderingArguments;
85 gdouble m_nLoadProgress;
86 bool m_bIsLoading;
87 bool m_bInit; // initializeForRendering() has been called
88 bool m_bCanZoomIn;
89 bool m_bCanZoomOut;
90 bool m_bUnipoll;
91 LibreOfficeKit* m_pOffice;
92 LibreOfficeKitDocument* m_pDocument;
93
94 std::unique_ptr<TileBuffer> m_pTileBuffer;
95 GThreadPool* lokThreadPool;
96
97 gfloat m_fZoom;
98 glong m_nDocumentWidthTwips;
99 glong m_nDocumentHeightTwips;
101 bool m_bEdit;
103 guint64 m_nLOKFeatures;
105 gint m_nParts;
107 GdkRectangle m_aVisibleCursor;
110 std::map<int, ViewRectangle> m_aViewCursors;
112 bool m_bCursorOverlayVisible;
114 bool m_bCursorVisible;
117 std::map<int, bool> m_aViewCursorVisibilities;
119 guint32 m_nLastButtonPressTime;
121 guint32 m_nLastButtonReleaseTime;
123 guint32 m_nLastButtonPressed;
125 guint32 m_nKeyModifier;
127 std::vector<GdkRectangle> m_aTextSelectionRectangles;
129 std::vector<GdkRectangle> m_aContentControlRectangles;
131 std::string m_aContentControlAlias;
134 std::map<int, ViewRectangles> m_aTextViewSelectionRectangles;
136 GdkRectangle m_aTextSelectionStart;
138 GdkRectangle m_aTextSelectionEnd;
139 GdkRectangle m_aGraphicSelection;
142 std::map<int, ViewRectangle> m_aGraphicViewSelections;
143 GdkRectangle m_aCellCursor;
146 std::map<int, ViewRectangle> m_aCellViewCursors;
147 bool m_bInDragGraphicSelection;
150 std::vector<std::pair<ViewRectangle, sal_uInt32>> m_aReferenceMarks;
151
155 cairo_surface_t* m_pHandleStart;
157 GdkRectangle m_aHandleStartRect;
159 bool m_bInDragStartHandle;
161 cairo_surface_t* m_pHandleMiddle;
163 GdkRectangle m_aHandleMiddleRect;
165 bool m_bInDragMiddleHandle;
167 cairo_surface_t* m_pHandleEnd;
169 GdkRectangle m_aHandleEndRect;
171 bool m_bInDragEndHandle;
173
177 GdkRectangle m_aGraphicHandleRects[8];
179 bool m_bInDragGraphicHandles[8];
181
183 int m_nViewId;
184
186 int m_nPartId;
187
189 LibreOfficeKitDocumentType m_eDocumentType;
190
193 int m_nTileSizeTwips;
194
195 GdkRectangle m_aVisibleArea;
196 bool m_bVisibleAreaSet;
197
199 guint m_nTimeoutId;
200
203 std::map<int, ViewRectangle> m_aViewLockRectangles;
204
205 LOKDocViewPrivateImpl()
206 : m_nLoadProgress(0),
207 m_bIsLoading(false),
208 m_bInit(false),
209 m_bCanZoomIn(true),
210 m_bCanZoomOut(true),
211 m_bUnipoll(false),
212 m_pOffice(nullptr),
213 m_pDocument(nullptr),
214 lokThreadPool(nullptr),
215 m_fZoom(0),
216 m_nDocumentWidthTwips(0),
217 m_nDocumentHeightTwips(0),
218 m_bEdit(false),
219 m_nLOKFeatures(0),
220 m_nParts(0),
221 m_aVisibleCursor({0, 0, 0, 0}),
222 m_bCursorOverlayVisible(false),
223 m_bCursorVisible(true),
224 m_nLastButtonPressTime(0),
225 m_nLastButtonReleaseTime(0),
226 m_nLastButtonPressed(0),
227 m_nKeyModifier(0),
228 m_aTextSelectionStart({0, 0, 0, 0}),
229 m_aTextSelectionEnd({0, 0, 0, 0}),
230 m_aGraphicSelection({0, 0, 0, 0}),
231 m_aCellCursor({0, 0, 0, 0}),
232 m_bInDragGraphicSelection(false),
233 m_pHandleStart(nullptr),
234 m_aHandleStartRect({0, 0, 0, 0}),
235 m_bInDragStartHandle(false),
236 m_pHandleMiddle(nullptr),
237 m_aHandleMiddleRect({0, 0, 0, 0}),
238 m_bInDragMiddleHandle(false),
239 m_pHandleEnd(nullptr),
240 m_aHandleEndRect({0, 0, 0, 0}),
241 m_bInDragEndHandle(false),
242 m_nViewId(0),
243 m_nPartId(0),
244 m_eDocumentType(LOK_DOCTYPE_OTHER),
245 m_nTileSizeTwips(0),
246 m_aVisibleArea({0, 0, 0, 0}),
247 m_bVisibleAreaSet(false),
248 m_nTimeoutId(0)
249 {
250 memset(&m_aGraphicHandleRects, 0, sizeof(m_aGraphicHandleRects));
251 memset(&m_bInDragGraphicHandles, 0, sizeof(m_bInDragGraphicHandles));
252 }
253
254 ~LOKDocViewPrivateImpl()
255 {
256 if (m_nTimeoutId)
257 g_source_remove(m_nTimeoutId);
258 }
259};
260
261// Must be run with g_aLOKMutex locked
262void setDocumentView(LibreOfficeKitDocument* pDoc, int viewId)
263{
264 assert(pDoc);
265 std::stringstream ss;
266 ss << "lok::Document::setView(" << viewId << ")";
267 g_info("%s", ss.str().c_str());
268 pDoc->pClass->setView(pDoc, viewId);
269}
270}
271
274{
275 LOKDocViewPrivateImpl* m_pImpl;
276
277 LOKDocViewPrivateImpl* operator->()
278 {
279 return m_pImpl;
280 }
281};
282
283enum
284{
304
307
308enum
309{
311
330
333
334static guint doc_view_signals[LAST_SIGNAL] = { 0 };
335static GParamSpec *properties[PROP_LAST] = { nullptr };
336
337static void lok_doc_view_initable_iface_init (GInitableIface *iface);
338static void callbackWorker (int nType, const char* pPayload, void* pData);
339static void updateClientZoom (LOKDocView *pDocView);
340
341#ifdef __GNUC__
342#pragma GCC diagnostic push
343#pragma GCC diagnostic ignored "-Wunused-function"
344#if defined __clang__
345#if __has_warning("-Wdeprecated-volatile")
346#pragma clang diagnostic ignored "-Wdeprecated-volatile"
347#endif
348#endif
349#endif
350G_DEFINE_TYPE_WITH_CODE (LOKDocView, lok_doc_view, GTK_TYPE_DRAWING_AREA,
351 G_ADD_PRIVATE (LOKDocView)
352 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, lok_doc_view_initable_iface_init));
353#ifdef __GNUC__
354#pragma GCC diagnostic pop
355#endif
356
357static LOKDocViewPrivate& getPrivate(LOKDocView* pDocView)
358{
359 LOKDocViewPrivate* priv = static_cast<LOKDocViewPrivate*>(lok_doc_view_get_instance_private(pDocView));
360 return *priv;
361}
362
363namespace {
364
366struct CallbackData
367{
368 int m_nType;
369 std::string m_aPayload;
370 LOKDocView* m_pDocView;
371
372 CallbackData(int nType, std::string aPayload, LOKDocView* pDocView)
373 : m_nType(nType),
374 m_aPayload(std::move(aPayload)),
375 m_pDocView(pDocView) {}
376};
377
378}
379
380static void
381LOKPostCommand (LOKDocView* pDocView,
382 const gchar* pCommand,
383 const gchar* pArguments,
384 bool bNotifyWhenFinished)
385{
386 LOKDocViewPrivate& priv = getPrivate(pDocView);
387 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
388 LOEvent* pLOEvent = new LOEvent(LOK_POST_COMMAND);
389 GError* error = nullptr;
390 pLOEvent->m_pCommand = g_strdup(pCommand);
391 pLOEvent->m_pArguments = g_strdup(pArguments);
392 pLOEvent->m_bNotifyWhenFinished = bNotifyWhenFinished;
393
394 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
395 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
396 if (error != nullptr)
397 {
398 g_warning("Unable to call LOK_POST_COMMAND: %s", error->message);
399 g_clear_error(&error);
400 }
401 g_object_unref(task);
402}
403
404static void
405doSearch(LOKDocView* pDocView, const char* pText, bool bBackwards, bool highlightAll)
406{
407 LOKDocViewPrivate& priv = getPrivate(pDocView);
408 if (!priv->m_pDocument)
409 return;
410
411 boost::property_tree::ptree aTree;
412 GtkWidget* drawingWidget = GTK_WIDGET(pDocView);
413 GdkWindow* drawingWindow = gtk_widget_get_window(drawingWidget);
414 if (!drawingWindow)
415 return;
416 std::shared_ptr<cairo_region_t> cairoVisRegion( gdk_window_get_visible_region(drawingWindow),
417 cairo_region_destroy);
418 cairo_rectangle_int_t cairoVisRect;
419 cairo_region_get_rectangle(cairoVisRegion.get(), 0, &cairoVisRect);
420 int x = pixelToTwip (cairoVisRect.x, priv->m_fZoom);
421 int y = pixelToTwip (cairoVisRect.y, priv->m_fZoom);
422
423 aTree.put(boost::property_tree::ptree::path_type("SearchItem.SearchString/type", '/'), "string");
424 aTree.put(boost::property_tree::ptree::path_type("SearchItem.SearchString/value", '/'), pText);
425 aTree.put(boost::property_tree::ptree::path_type("SearchItem.Backward/type", '/'), "boolean");
426 aTree.put(boost::property_tree::ptree::path_type("SearchItem.Backward/value", '/'), bBackwards);
427 if (highlightAll)
428 {
429 aTree.put(boost::property_tree::ptree::path_type("SearchItem.Command/type", '/'), "unsigned short");
430 // SvxSearchCmd::FIND_ALL
431 aTree.put(boost::property_tree::ptree::path_type("SearchItem.Command/value", '/'), "1");
432 }
433
434 aTree.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointX/type", '/'), "long");
435 aTree.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointX/value", '/'), x);
436 aTree.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointY/type", '/'), "long");
437 aTree.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointY/value", '/'), y);
438
439 std::stringstream aStream;
440 boost::property_tree::write_json(aStream, aTree);
441
442 LOKPostCommand (pDocView, ".uno:ExecuteSearch", aStream.str().c_str(), false);
443}
444
445static bool
446isEmptyRectangle(const GdkRectangle& rRectangle)
447{
448 return rRectangle.x == 0 && rRectangle.y == 0 && rRectangle.width == 0 && rRectangle.height == 0;
449}
450
452static bool
453handleTextSelectionOnButtonPress(GdkRectangle& aClick, LOKDocView* pDocView) {
454 LOKDocViewPrivate& priv = getPrivate(pDocView);
455
456 if (gdk_rectangle_intersect(&aClick, &priv->m_aHandleStartRect, nullptr))
457 {
458 g_info("LOKDocView_Impl::signalButton: start of drag start handle");
459 priv->m_bInDragStartHandle = true;
460 return true;
461 }
462 else if (gdk_rectangle_intersect(&aClick, &priv->m_aHandleMiddleRect, nullptr))
463 {
464 g_info("LOKDocView_Impl::signalButton: start of drag middle handle");
465 priv->m_bInDragMiddleHandle = true;
466 return true;
467 }
468 else if (gdk_rectangle_intersect(&aClick, &priv->m_aHandleEndRect, nullptr))
469 {
470 g_info("LOKDocView_Impl::signalButton: start of drag end handle");
471 priv->m_bInDragEndHandle = true;
472 return true;
473 }
474
475 return false;
476}
477
479static bool
480handleGraphicSelectionOnButtonPress(GdkRectangle& aClick, LOKDocView* pDocView) {
481 LOKDocViewPrivate& priv = getPrivate(pDocView);
482 GError* error = nullptr;
483
484 for (int i = 0; i < GRAPHIC_HANDLE_COUNT; ++i)
485 {
486 if (gdk_rectangle_intersect(&aClick, &priv->m_aGraphicHandleRects[i], nullptr))
487 {
488 g_info("LOKDocView_Impl::signalButton: start of drag graphic handle #%d", i);
489 priv->m_bInDragGraphicHandles[i] = true;
490
491 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
493 pLOEvent->m_nSetGraphicSelectionType = LOK_SETGRAPHICSELECTION_START;
494 pLOEvent->m_nSetGraphicSelectionX = pixelToTwip(priv->m_aGraphicHandleRects[i].x + priv->m_aGraphicHandleRects[i].width / 2, priv->m_fZoom);
495 pLOEvent->m_nSetGraphicSelectionY = pixelToTwip(priv->m_aGraphicHandleRects[i].y + priv->m_aGraphicHandleRects[i].height / 2, priv->m_fZoom);
496 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
497
498 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
499 if (error != nullptr)
500 {
501 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error->message);
502 g_clear_error(&error);
503 }
504 g_object_unref(task);
505
506 return true;
507 }
508 }
509
510 return false;
511}
512
514static bool
516 LOKDocViewPrivate& priv = getPrivate(pDocView);
517
518 if (priv->m_bInDragStartHandle)
519 {
520 g_info("LOKDocView_Impl::signalButton: end of drag start handle");
521 priv->m_bInDragStartHandle = false;
522 return true;
523 }
524 else if (priv->m_bInDragMiddleHandle)
525 {
526 g_info("LOKDocView_Impl::signalButton: end of drag middle handle");
527 priv->m_bInDragMiddleHandle = false;
528 return true;
529 }
530 else if (priv->m_bInDragEndHandle)
531 {
532 g_info("LOKDocView_Impl::signalButton: end of drag end handle");
533 priv->m_bInDragEndHandle = false;
534 return true;
535 }
536
537 return false;
538}
539
541static bool
542handleGraphicSelectionOnButtonRelease(LOKDocView* pDocView, GdkEventButton* pEvent) {
543 LOKDocViewPrivate& priv = getPrivate(pDocView);
544 GError* error = nullptr;
545
546 for (int i = 0; i < GRAPHIC_HANDLE_COUNT; ++i)
547 {
548 if (priv->m_bInDragGraphicHandles[i])
549 {
550 g_info("LOKDocView_Impl::signalButton: end of drag graphic handle #%d", i);
551 priv->m_bInDragGraphicHandles[i] = false;
552
553 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
555 pLOEvent->m_nSetGraphicSelectionType = LOK_SETGRAPHICSELECTION_END;
556 pLOEvent->m_nSetGraphicSelectionX = pixelToTwip(pEvent->x, priv->m_fZoom);
557 pLOEvent->m_nSetGraphicSelectionY = pixelToTwip(pEvent->y, priv->m_fZoom);
558 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
559
560 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
561 if (error != nullptr)
562 {
563 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error->message);
564 g_clear_error(&error);
565 }
566 g_object_unref(task);
567
568 return true;
569 }
570 }
571
572 if (!priv->m_bInDragGraphicSelection)
573 return false;
574
575 g_info("LOKDocView_Impl::signalButton: end of drag graphic selection");
576 priv->m_bInDragGraphicSelection = false;
577
578 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
580 pLOEvent->m_nSetGraphicSelectionType = LOK_SETGRAPHICSELECTION_END;
581 pLOEvent->m_nSetGraphicSelectionX = pixelToTwip(pEvent->x, priv->m_fZoom);
582 pLOEvent->m_nSetGraphicSelectionY = pixelToTwip(pEvent->y, priv->m_fZoom);
583 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
584
585 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
586 if (error != nullptr)
587 {
588 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error->message);
589 g_clear_error(&error);
590 }
591 g_object_unref(task);
592
593 return true;
594}
595
596static void
598{
599 GTask* task = G_TASK(data);
600 LOKDocView* pDocView = LOK_DOC_VIEW(g_task_get_source_object(task));
601 LOKDocViewPrivate& priv = getPrivate(pDocView);
602 LOEvent* pLOEvent = static_cast<LOEvent*>(g_task_get_task_data(task));
603 gint nScaleFactor = gtk_widget_get_scale_factor(GTK_WIDGET(pDocView));
604 gint nTileSizePixelsScaled = nTileSizePixels * nScaleFactor;
605
606 std::scoped_lock<std::mutex> aGuard(g_aLOKMutex);
607 setDocumentView(priv->m_pDocument, priv->m_nViewId);
608 std::stringstream ss;
609
610 if (priv->m_nTileSizeTwips)
611 {
612 ss.str(std::string());
613 ss << "lok::Document::setClientZoom(" << nTileSizePixelsScaled << ", " << nTileSizePixelsScaled << ", " << priv->m_nTileSizeTwips << ", " << priv->m_nTileSizeTwips << ")";
614 g_info("%s", ss.str().c_str());
615 priv->m_pDocument->pClass->setClientZoom(priv->m_pDocument,
616 nTileSizePixelsScaled,
617 nTileSizePixelsScaled,
618 priv->m_nTileSizeTwips,
619 priv->m_nTileSizeTwips);
620 priv->m_nTileSizeTwips = 0;
621 }
622 if (priv->m_bVisibleAreaSet)
623 {
624 ss.str(std::string());
625 ss << "lok::Document::setClientVisibleArea(" << priv->m_aVisibleArea.x << ", " << priv->m_aVisibleArea.y << ", ";
626 ss << priv->m_aVisibleArea.width << ", " << priv->m_aVisibleArea.height << ")";
627 g_info("%s", ss.str().c_str());
628 priv->m_pDocument->pClass->setClientVisibleArea(priv->m_pDocument,
629 priv->m_aVisibleArea.x,
630 priv->m_aVisibleArea.y,
631 priv->m_aVisibleArea.width,
632 priv->m_aVisibleArea.height);
633 priv->m_bVisibleAreaSet = false;
634 }
635
636 ss.str(std::string());
637 ss << "lok::Document::postKeyEvent(" << pLOEvent->m_nKeyEvent << ", " << pLOEvent->m_nCharCode << ", " << pLOEvent->m_nKeyCode << ")";
638 g_info("%s", ss.str().c_str());
639 priv->m_pDocument->pClass->postKeyEvent(priv->m_pDocument,
640 pLOEvent->m_nKeyEvent,
641 pLOEvent->m_nCharCode,
642 pLOEvent->m_nKeyCode);
643}
644
645static gboolean
646signalKey (GtkWidget* pWidget, GdkEventKey* pEvent)
647{
648 LOKDocView* pDocView = LOK_DOC_VIEW(pWidget);
649 LOKDocViewPrivate& priv = getPrivate(pDocView);
650 int nCharCode = 0;
651 int nKeyCode = 0;
652 GError* error = nullptr;
653
654 if (!priv->m_bEdit)
655 {
656 g_info("signalKey: not in edit mode, ignore");
657 return FALSE;
658 }
659
660 priv->m_nKeyModifier &= KEY_MOD2;
661 switch (pEvent->keyval)
662 {
663 case GDK_KEY_BackSpace:
664 nKeyCode = com::sun::star::awt::Key::BACKSPACE;
665 break;
666 case GDK_KEY_Delete:
667 nKeyCode = com::sun::star::awt::Key::DELETE;
668 break;
669 case GDK_KEY_Return:
670 case GDK_KEY_KP_Enter:
671 nKeyCode = com::sun::star::awt::Key::RETURN;
672 break;
673 case GDK_KEY_Escape:
674 nKeyCode = com::sun::star::awt::Key::ESCAPE;
675 break;
676 case GDK_KEY_Tab:
677 nKeyCode = com::sun::star::awt::Key::TAB;
678 break;
679 case GDK_KEY_Down:
680 nKeyCode = com::sun::star::awt::Key::DOWN;
681 break;
682 case GDK_KEY_Up:
683 nKeyCode = com::sun::star::awt::Key::UP;
684 break;
685 case GDK_KEY_Left:
686 nKeyCode = com::sun::star::awt::Key::LEFT;
687 break;
688 case GDK_KEY_Right:
689 nKeyCode = com::sun::star::awt::Key::RIGHT;
690 break;
691 case GDK_KEY_Page_Down:
692 nKeyCode = com::sun::star::awt::Key::PAGEDOWN;
693 break;
694 case GDK_KEY_Page_Up:
695 nKeyCode = com::sun::star::awt::Key::PAGEUP;
696 break;
697 case GDK_KEY_Insert:
698 nKeyCode = com::sun::star::awt::Key::INSERT;
699 break;
700 case GDK_KEY_Shift_L:
701 case GDK_KEY_Shift_R:
702 if (pEvent->type == GDK_KEY_PRESS)
703 priv->m_nKeyModifier |= KEY_SHIFT;
704 break;
705 case GDK_KEY_Control_L:
706 case GDK_KEY_Control_R:
707 if (pEvent->type == GDK_KEY_PRESS)
708 priv->m_nKeyModifier |= KEY_MOD1;
709 break;
710 case GDK_KEY_Alt_L:
711 case GDK_KEY_Alt_R:
712 if (pEvent->type == GDK_KEY_PRESS)
713 priv->m_nKeyModifier |= KEY_MOD2;
714 else
715 priv->m_nKeyModifier &= ~KEY_MOD2;
716 break;
717 default:
718 if (pEvent->keyval >= GDK_KEY_F1 && pEvent->keyval <= GDK_KEY_F26)
719 nKeyCode = com::sun::star::awt::Key::F1 + (pEvent->keyval - GDK_KEY_F1);
720 else
721 nCharCode = gdk_keyval_to_unicode(pEvent->keyval);
722 }
723
724 // rsc is not public API, but should be good enough for debugging purposes.
725 // If this is needed for real, then probably a new param of type
726 // css::awt::KeyModifier is needed in postKeyEvent().
727 if (pEvent->state & GDK_SHIFT_MASK)
728 nKeyCode |= KEY_SHIFT;
729
730 if (pEvent->state & GDK_CONTROL_MASK)
731 nKeyCode |= KEY_MOD1;
732
733 if (priv->m_nKeyModifier & KEY_MOD2)
734 nKeyCode |= KEY_MOD2;
735
736 if (nKeyCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2)) {
737 if (pEvent->keyval >= GDK_KEY_a && pEvent->keyval <= GDK_KEY_z)
738 {
739 nKeyCode |= 512 + (pEvent->keyval - GDK_KEY_a);
740 }
741 else if (pEvent->keyval >= GDK_KEY_A && pEvent->keyval <= GDK_KEY_Z) {
742 nKeyCode |= 512 + (pEvent->keyval - GDK_KEY_A);
743 }
744 else if (pEvent->keyval >= GDK_KEY_0 && pEvent->keyval <= GDK_KEY_9) {
745 nKeyCode |= 256 + (pEvent->keyval - GDK_KEY_0);
746 }
747 }
748
749 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
750 LOEvent* pLOEvent = new LOEvent(LOK_POST_KEY);
751 pLOEvent->m_nKeyEvent = pEvent->type == GDK_KEY_RELEASE ? LOK_KEYEVENT_KEYUP : LOK_KEYEVENT_KEYINPUT;
752 pLOEvent->m_nCharCode = nCharCode;
753 pLOEvent->m_nKeyCode = nKeyCode;
754 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
755 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
756 if (error != nullptr)
757 {
758 g_warning("Unable to call LOK_POST_KEY: %s", error->message);
759 g_clear_error(&error);
760 }
761 g_object_unref(task);
762
763 return FALSE;
764}
765
766static gboolean
767handleTimeout (gpointer pData)
768{
769 LOKDocView* pDocView = LOK_DOC_VIEW (pData);
770 LOKDocViewPrivate& priv = getPrivate(pDocView);
771
772 if (priv->m_bEdit)
773 {
774 if (priv->m_bCursorOverlayVisible)
775 priv->m_bCursorOverlayVisible = false;
776 else
777 priv->m_bCursorOverlayVisible = true;
778 gtk_widget_queue_draw(GTK_WIDGET(pDocView));
779 }
780
781 return G_SOURCE_CONTINUE;
782}
783
784static void
785commandChanged(LOKDocView* pDocView, const std::string& rString)
786{
787 g_signal_emit(pDocView, doc_view_signals[COMMAND_CHANGED], 0, rString.c_str());
788}
789
790static void
791searchNotFound(LOKDocView* pDocView, const std::string& rString)
792{
793 g_signal_emit(pDocView, doc_view_signals[SEARCH_NOT_FOUND], 0, rString.c_str());
794}
795
796static void searchResultCount(LOKDocView* pDocView, const std::string& rString)
797{
798 g_signal_emit(pDocView, doc_view_signals[SEARCH_RESULT_COUNT], 0, rString.c_str());
799}
800
801static void commandResult(LOKDocView* pDocView, const std::string& rString)
802{
803 g_signal_emit(pDocView, doc_view_signals[COMMAND_RESULT], 0, rString.c_str());
804}
805
806static void addressChanged(LOKDocView* pDocView, const std::string& rString)
807{
808 g_signal_emit(pDocView, doc_view_signals[ADDRESS_CHANGED], 0, rString.c_str());
809}
810
811static void formulaChanged(LOKDocView* pDocView, const std::string& rString)
812{
813 g_signal_emit(pDocView, doc_view_signals[FORMULA_CHANGED], 0, rString.c_str());
814}
815
816static void reportError(LOKDocView* /*pDocView*/, const std::string& rString)
817{
818 GtkWidget *dialog = gtk_message_dialog_new(nullptr,
819 GTK_DIALOG_DESTROY_WITH_PARENT,
820 GTK_MESSAGE_ERROR,
821 GTK_BUTTONS_CLOSE,
822 "%s",
823 rString.c_str());
824 gtk_dialog_run(GTK_DIALOG(dialog));
825 gtk_widget_destroy(dialog);
826}
827
828static void
829setPart(LOKDocView* pDocView, const std::string& rString)
830{
831 LOKDocViewPrivate& priv = getPrivate(pDocView);
832 priv->m_nPartId = std::stoi(rString);
833 g_signal_emit(pDocView, doc_view_signals[PART_CHANGED], 0, priv->m_nPartId);
834}
835
836static void
837hyperlinkClicked(LOKDocView* pDocView, const std::string& rString)
838{
839 g_signal_emit(pDocView, doc_view_signals[HYPERLINK_CLICKED], 0, rString.c_str());
840}
841
843static gboolean queueDraw(gpointer pData)
844{
845 GtkWidget* pWidget = static_cast<GtkWidget*>(pData);
846
847 gtk_widget_queue_draw(pWidget);
848
849 return G_SOURCE_REMOVE;
850}
851
853static std::string getAuthorRenderingArgument(LOKDocViewPrivate& priv)
854{
855 std::stringstream aStream;
856 aStream << priv->m_aRenderingArguments;
857 boost::property_tree::ptree aTree;
858 boost::property_tree::read_json(aStream, aTree);
859 std::string aRet;
860 for (const auto& rPair : aTree)
861 {
862 if (rPair.first == ".uno:Author")
863 {
864 aRet = rPair.second.get<std::string>("value");
865 break;
866 }
867 }
868 return aRet;
869}
870
872static std::map<std::string, int> g_aAuthorViews;
873
874static void refreshSize(LOKDocView* pDocView)
875{
876 LOKDocViewPrivate& priv = getPrivate(pDocView);
877
878 priv->m_pDocument->pClass->getDocumentSize(priv->m_pDocument, &priv->m_nDocumentWidthTwips, &priv->m_nDocumentHeightTwips);
879 float zoom = priv->m_fZoom;
880 gint nScaleFactor = gtk_widget_get_scale_factor(GTK_WIDGET(pDocView));
881 gint nTileSizePixelsScaled = nTileSizePixels * nScaleFactor;
882 long nDocumentWidthTwips = priv->m_nDocumentWidthTwips;
883 long nDocumentHeightTwips = priv->m_nDocumentHeightTwips;
884 long nDocumentWidthPixels = twipToPixel(nDocumentWidthTwips, zoom);
885 long nDocumentHeightPixels = twipToPixel(nDocumentHeightTwips, zoom);
886
887 // Total number of columns in this document.
888 guint nColumns = ceil(static_cast<double>(nDocumentWidthPixels) / nTileSizePixelsScaled);
889 priv->m_pTileBuffer = std::make_unique<TileBuffer>(nColumns, nScaleFactor);
890 gtk_widget_set_size_request(GTK_WIDGET(pDocView),
891 nDocumentWidthPixels,
892 nDocumentHeightPixels);
893}
894
896static gboolean postDocumentLoad(gpointer pData)
897{
898 LOKDocView* pLOKDocView = static_cast<LOKDocView*>(pData);
899 LOKDocViewPrivate& priv = getPrivate(pLOKDocView);
900
901 std::unique_lock<std::mutex> aGuard(g_aLOKMutex);
902 priv->m_pDocument->pClass->initializeForRendering(priv->m_pDocument, priv->m_aRenderingArguments.c_str());
903 // This returns the view id of the "current" view, but sadly if you load multiple documents that
904 // is apparently not a view showing the most recently loaded document. Not much we can do here,
905 // though. If that is fixed, this comment becomes incorrect.
906 priv->m_nViewId = priv->m_pDocument->pClass->getView(priv->m_pDocument);
907 g_aAuthorViews[getAuthorRenderingArgument(priv)] = priv->m_nViewId;
908 priv->m_pDocument->pClass->registerCallback(priv->m_pDocument, callbackWorker, pLOKDocView);
909 priv->m_nParts = priv->m_pDocument->pClass->getParts(priv->m_pDocument);
910 aGuard.unlock();
911 priv->m_nTimeoutId = g_timeout_add(600, handleTimeout, pLOKDocView);
912
913 refreshSize(pLOKDocView);
914
915 gtk_widget_set_can_focus(GTK_WIDGET(pLOKDocView), true);
916 gtk_widget_grab_focus(GTK_WIDGET(pLOKDocView));
917 lok_doc_view_set_zoom(pLOKDocView, 1.0);
918
919 // we are completely loaded
920 priv->m_bInit = true;
921 g_object_notify_by_pspec(G_OBJECT(pLOKDocView), properties[PROP_IS_INITIALIZED]);
922
923 return G_SOURCE_REMOVE;
924}
925
927static gboolean
928globalCallback (gpointer pData)
929{
930 CallbackData* pCallback = static_cast<CallbackData*>(pData);
931 LOKDocViewPrivate& priv = getPrivate(pCallback->m_pDocView);
932 bool bModify = false;
933
934 switch (pCallback->m_nType)
935 {
936 case LOK_CALLBACK_STATUS_INDICATOR_START:
937 {
938 priv->m_nLoadProgress = 0.0;
939 g_signal_emit (pCallback->m_pDocView, doc_view_signals[LOAD_CHANGED], 0, 0.0);
940 }
941 break;
942 case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE:
943 {
944 priv->m_nLoadProgress = static_cast<gdouble>(std::stoi(pCallback->m_aPayload)/100.0);
945 g_signal_emit (pCallback->m_pDocView, doc_view_signals[LOAD_CHANGED], 0, priv->m_nLoadProgress);
946 }
947 break;
948 case LOK_CALLBACK_STATUS_INDICATOR_FINISH:
949 {
950 priv->m_nLoadProgress = 1.0;
951 g_signal_emit (pCallback->m_pDocView, doc_view_signals[LOAD_CHANGED], 0, 1.0);
952 }
953 break;
954 case LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY:
955 bModify = true;
956 [[fallthrough]];
957 case LOK_CALLBACK_DOCUMENT_PASSWORD:
958 {
959 char const*const pURL(pCallback->m_aPayload.c_str());
960 g_signal_emit (pCallback->m_pDocView, doc_view_signals[PASSWORD_REQUIRED], 0, pURL, bModify);
961 }
962 break;
963 case LOK_CALLBACK_ERROR:
964 {
965 reportError(pCallback->m_pDocView, pCallback->m_aPayload);
966 }
967 break;
968 case LOK_CALLBACK_SIGNATURE_STATUS:
969 {
970 // TODO
971 }
972 break;
973 default:
974 g_assert(false);
975 break;
976 }
977 delete pCallback;
978
979 return G_SOURCE_REMOVE;
980}
981
982static void
983globalCallbackWorker(int nType, const char* pPayload, void* pData)
984{
985 LOKDocView* pDocView = LOK_DOC_VIEW (pData);
986
987 CallbackData* pCallback = new CallbackData(nType, pPayload ? pPayload : "(nil)", pDocView);
988 g_info("LOKDocView_Impl::globalCallbackWorkerImpl: %s, '%s'", lokCallbackTypeToString(nType), pPayload);
989 gdk_threads_add_idle(globalCallback, pCallback);
990}
991
992static GdkRectangle
993payloadToRectangle (LOKDocView* pDocView, const char* pPayload)
994{
995 LOKDocViewPrivate& priv = getPrivate(pDocView);
996 GdkRectangle aRet;
997 // x, y, width, height, part number.
998 gchar** ppCoordinates = g_strsplit(pPayload, ", ", 5);
999 gchar** ppCoordinate = ppCoordinates;
1000
1001 aRet.width = aRet.height = aRet.x = aRet.y = 0;
1002
1003 if (!*ppCoordinate)
1004 {
1005 g_strfreev(ppCoordinates);
1006 return aRet;
1007 }
1008 aRet.x = atoi(*ppCoordinate);
1009 if (aRet.x < 0)
1010 aRet.x = 0;
1011 ++ppCoordinate;
1012 if (!*ppCoordinate)
1013 {
1014 g_strfreev(ppCoordinates);
1015 return aRet;
1016 }
1017 aRet.y = atoi(*ppCoordinate);
1018 if (aRet.y < 0)
1019 aRet.y = 0;
1020 ++ppCoordinate;
1021 if (!*ppCoordinate)
1022 {
1023 g_strfreev(ppCoordinates);
1024 return aRet;
1025 }
1026 long l = atol(*ppCoordinate);
1027 if (l > std::numeric_limits<int>::max())
1028 aRet.width = std::numeric_limits<int>::max();
1029 else
1030 aRet.width = l;
1031 if (aRet.x + aRet.width > priv->m_nDocumentWidthTwips)
1032 aRet.width = priv->m_nDocumentWidthTwips - aRet.x;
1033 ++ppCoordinate;
1034 if (!*ppCoordinate)
1035 {
1036 g_strfreev(ppCoordinates);
1037 return aRet;
1038 }
1039 l = atol(*ppCoordinate);
1040 if (l > std::numeric_limits<int>::max())
1041 aRet.height = std::numeric_limits<int>::max();
1042 else
1043 aRet.height = l;
1044 if (aRet.y + aRet.height > priv->m_nDocumentHeightTwips)
1045 aRet.height = priv->m_nDocumentHeightTwips - aRet.y;
1046
1047 g_strfreev(ppCoordinates);
1048 return aRet;
1049}
1050
1051static std::vector<GdkRectangle>
1052payloadToRectangles(LOKDocView* pDocView, const char* pPayload)
1053{
1054 std::vector<GdkRectangle> aRet;
1055
1056 if (g_strcmp0(pPayload, "EMPTY") == 0)
1057 return aRet;
1058
1059 gchar** ppRectangles = g_strsplit(pPayload, "; ", 0);
1060 for (gchar** ppRectangle = ppRectangles; *ppRectangle; ++ppRectangle)
1061 aRet.push_back(payloadToRectangle(pDocView, *ppRectangle));
1062 g_strfreev(ppRectangles);
1063
1064 return aRet;
1065}
1066
1067
1068static void
1069setTilesInvalid (LOKDocView* pDocView, const GdkRectangle& rRectangle)
1070{
1071 LOKDocViewPrivate& priv = getPrivate(pDocView);
1072 GdkRectangle aRectanglePixels;
1073 GdkPoint aStart, aEnd;
1074 gint nScaleFactor = gtk_widget_get_scale_factor(GTK_WIDGET(pDocView));
1075 gint nTileSizePixelsScaled = nTileSizePixels * nScaleFactor;
1076
1077 aRectanglePixels.x = twipToPixel(rRectangle.x, priv->m_fZoom) * nScaleFactor;
1078 aRectanglePixels.y = twipToPixel(rRectangle.y, priv->m_fZoom) * nScaleFactor;
1079 aRectanglePixels.width = twipToPixel(rRectangle.width, priv->m_fZoom) * nScaleFactor;
1080 aRectanglePixels.height = twipToPixel(rRectangle.height, priv->m_fZoom) * nScaleFactor;
1081
1082 aStart.x = aRectanglePixels.y / nTileSizePixelsScaled;
1083 aStart.y = aRectanglePixels.x / nTileSizePixelsScaled;
1084 aEnd.x = (aRectanglePixels.y + aRectanglePixels.height + nTileSizePixelsScaled) / nTileSizePixelsScaled;
1085 aEnd.y = (aRectanglePixels.x + aRectanglePixels.width + nTileSizePixelsScaled) / nTileSizePixelsScaled;
1086 for (int i = aStart.x; i < aEnd.x; i++)
1087 {
1088 for (int j = aStart.y; j < aEnd.y; j++)
1089 {
1090 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
1091 priv->m_pTileBuffer->setInvalid(i, j, priv->m_fZoom, task, priv->lokThreadPool);
1092 g_object_unref(task);
1093 }
1094 }
1095}
1096
1097static gboolean
1098callback (gpointer pData)
1099{
1100 CallbackData* pCallback = static_cast<CallbackData*>(pData);
1101 LOKDocView* pDocView = LOK_DOC_VIEW (pCallback->m_pDocView);
1102 LOKDocViewPrivate& priv = getPrivate(pDocView);
1103
1104 //callback registered before the widget was destroyed.
1105 //Use existence of lokThreadPool as flag it was torn down
1106 if (!priv->lokThreadPool)
1107 {
1108 delete pCallback;
1109 return G_SOURCE_REMOVE;
1110 }
1111
1112 switch (static_cast<LibreOfficeKitCallbackType>(pCallback->m_nType))
1113 {
1114 case LOK_CALLBACK_INVALIDATE_TILES:
1115 {
1116 if (pCallback->m_aPayload.compare(0, 5, "EMPTY") != 0) // payload doesn't start with "EMPTY"
1117 {
1118 GdkRectangle aRectangle = payloadToRectangle(pDocView, pCallback->m_aPayload.c_str());
1119 setTilesInvalid(pDocView, aRectangle);
1120 }
1121 else
1122 priv->m_pTileBuffer->resetAllTiles();
1123
1124 gtk_widget_queue_draw(GTK_WIDGET(pDocView));
1125 }
1126 break;
1127 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
1128 {
1129
1130 std::stringstream aStream(pCallback->m_aPayload);
1131 boost::property_tree::ptree aTree;
1132 boost::property_tree::read_json(aStream, aTree);
1133 const std::string& rRectangle = aTree.get<std::string>("rectangle");
1134 int nViewId = aTree.get<int>("viewId");
1135
1136 priv->m_aVisibleCursor = payloadToRectangle(pDocView, rRectangle.c_str());
1137 priv->m_bCursorOverlayVisible = true;
1138 if(nViewId == priv->m_nViewId)
1139 {
1140 g_signal_emit(pDocView, doc_view_signals[CURSOR_CHANGED], 0,
1141 priv->m_aVisibleCursor.x,
1142 priv->m_aVisibleCursor.y,
1143 priv->m_aVisibleCursor.width,
1144 priv->m_aVisibleCursor.height);
1145 }
1146 gtk_widget_queue_draw(GTK_WIDGET(pDocView));
1147 }
1148 break;
1149 case LOK_CALLBACK_TEXT_SELECTION:
1150 {
1151 priv->m_aTextSelectionRectangles = payloadToRectangles(pDocView, pCallback->m_aPayload.c_str());
1152 bool bIsTextSelected = !priv->m_aTextSelectionRectangles.empty();
1153 // In case the selection is empty, then we get no LOK_CALLBACK_TEXT_SELECTION_START/END events.
1154 if (!bIsTextSelected)
1155 {
1156 memset(&priv->m_aTextSelectionStart, 0, sizeof(priv->m_aTextSelectionStart));
1157 memset(&priv->m_aHandleStartRect, 0, sizeof(priv->m_aHandleStartRect));
1158 memset(&priv->m_aTextSelectionEnd, 0, sizeof(priv->m_aTextSelectionEnd));
1159 memset(&priv->m_aHandleEndRect, 0, sizeof(priv->m_aHandleEndRect));
1160 }
1161 else
1162 memset(&priv->m_aHandleMiddleRect, 0, sizeof(priv->m_aHandleMiddleRect));
1163
1164 g_signal_emit(pDocView, doc_view_signals[TEXT_SELECTION], 0, bIsTextSelected);
1165 gtk_widget_queue_draw(GTK_WIDGET(pDocView));
1166 }
1167 break;
1168 case LOK_CALLBACK_TEXT_SELECTION_START:
1169 {
1170 priv->m_aTextSelectionStart = payloadToRectangle(pDocView, pCallback->m_aPayload.c_str());
1171 }
1172 break;
1173 case LOK_CALLBACK_TEXT_SELECTION_END:
1174 {
1175 priv->m_aTextSelectionEnd = payloadToRectangle(pDocView, pCallback->m_aPayload.c_str());
1176 }
1177 break;
1178 case LOK_CALLBACK_CURSOR_VISIBLE:
1179 {
1180 priv->m_bCursorVisible = pCallback->m_aPayload == "true";
1181 }
1182 break;
1183 case LOK_CALLBACK_MOUSE_POINTER:
1184 {
1185 // We do not want the cursor to get changed in view-only mode
1186 if (priv->m_bEdit)
1187 {
1188 // The gtk docs claim that most css cursors should be supported, however
1189 // on my system at least this is not true and many cursors are unsupported.
1190 // In this case pCursor = null, which results in the default cursor
1191 // being set.
1192 GdkCursor* pCursor = gdk_cursor_new_from_name(gtk_widget_get_display(GTK_WIDGET(pDocView)),
1193 pCallback->m_aPayload.c_str());
1194 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(pDocView)), pCursor);
1195 }
1196 }
1197 break;
1198 case LOK_CALLBACK_GRAPHIC_SELECTION:
1199 {
1200 if (pCallback->m_aPayload != "EMPTY")
1201 priv->m_aGraphicSelection = payloadToRectangle(pDocView, pCallback->m_aPayload.c_str());
1202 else
1203 memset(&priv->m_aGraphicSelection, 0, sizeof(priv->m_aGraphicSelection));
1204 gtk_widget_queue_draw(GTK_WIDGET(pDocView));
1205 }
1206 break;
1207 case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
1208 {
1209 std::stringstream aStream(pCallback->m_aPayload);
1210 boost::property_tree::ptree aTree;
1211 boost::property_tree::read_json(aStream, aTree);
1212 int nViewId = aTree.get<int>("viewId");
1213 int nPart = aTree.get<int>("part");
1214 const std::string& rRectangle = aTree.get<std::string>("selection");
1215 if (rRectangle != "EMPTY")
1216 priv->m_aGraphicViewSelections[nViewId] = ViewRectangle(nPart, payloadToRectangle(pDocView, rRectangle.c_str()));
1217 else
1218 {
1219 auto it = priv->m_aGraphicViewSelections.find(nViewId);
1220 if (it != priv->m_aGraphicViewSelections.end())
1221 priv->m_aGraphicViewSelections.erase(it);
1222 }
1223 gtk_widget_queue_draw(GTK_WIDGET(pDocView));
1224 break;
1225 }
1226 break;
1227 case LOK_CALLBACK_CELL_CURSOR:
1228 {
1229 if (pCallback->m_aPayload != "EMPTY")
1230 priv->m_aCellCursor = payloadToRectangle(pDocView, pCallback->m_aPayload.c_str());
1231 else
1232 memset(&priv->m_aCellCursor, 0, sizeof(priv->m_aCellCursor));
1233 gtk_widget_queue_draw(GTK_WIDGET(pDocView));
1234 }
1235 break;
1236 case LOK_CALLBACK_HYPERLINK_CLICKED:
1237 {
1238 hyperlinkClicked(pDocView, pCallback->m_aPayload);
1239 }
1240 break;
1241 case LOK_CALLBACK_STATE_CHANGED:
1242 {
1243 commandChanged(pDocView, pCallback->m_aPayload);
1244 }
1245 break;
1246 case LOK_CALLBACK_SEARCH_NOT_FOUND:
1247 {
1248 searchNotFound(pDocView, pCallback->m_aPayload);
1249 }
1250 break;
1251 case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED:
1252 {
1253 refreshSize(pDocView);
1254 g_signal_emit(pDocView, doc_view_signals[SIZE_CHANGED], 0, nullptr);
1255 }
1256 break;
1257 case LOK_CALLBACK_SET_PART:
1258 {
1259 setPart(pDocView, pCallback->m_aPayload);
1260 }
1261 break;
1262 case LOK_CALLBACK_SEARCH_RESULT_SELECTION:
1263 {
1264 boost::property_tree::ptree aTree;
1265 std::stringstream aStream(pCallback->m_aPayload);
1266 boost::property_tree::read_json(aStream, aTree);
1267 int nCount = aTree.get_child("searchResultSelection").size();
1268 searchResultCount(pDocView, std::to_string(nCount));
1269 }
1270 break;
1271 case LOK_CALLBACK_UNO_COMMAND_RESULT:
1272 {
1273 commandResult(pDocView, pCallback->m_aPayload);
1274 }
1275 break;
1276 case LOK_CALLBACK_CELL_ADDRESS:
1277 {
1278 addressChanged(pDocView, pCallback->m_aPayload);
1279 }
1280 break;
1281 case LOK_CALLBACK_CELL_FORMULA:
1282 {
1283 formulaChanged(pDocView, pCallback->m_aPayload);
1284 }
1285 break;
1286 case LOK_CALLBACK_ERROR:
1287 {
1288 reportError(pDocView, pCallback->m_aPayload);
1289 }
1290 break;
1291 case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
1292 {
1293 std::stringstream aStream(pCallback->m_aPayload);
1294 boost::property_tree::ptree aTree;
1295 boost::property_tree::read_json(aStream, aTree);
1296 int nViewId = aTree.get<int>("viewId");
1297 int nPart = aTree.get<int>("part");
1298 const std::string& rRectangle = aTree.get<std::string>("rectangle");
1299 priv->m_aViewCursors[nViewId] = ViewRectangle(nPart, payloadToRectangle(pDocView, rRectangle.c_str()));
1300 gtk_widget_queue_draw(GTK_WIDGET(pDocView));
1301 break;
1302 }
1303 case LOK_CALLBACK_TEXT_VIEW_SELECTION:
1304 {
1305 std::stringstream aStream(pCallback->m_aPayload);
1306 boost::property_tree::ptree aTree;
1307 boost::property_tree::read_json(aStream, aTree);
1308 int nViewId = aTree.get<int>("viewId");
1309 int nPart = aTree.get<int>("part");
1310 const std::string& rSelection = aTree.get<std::string>("selection");
1311 priv->m_aTextViewSelectionRectangles[nViewId] = ViewRectangles(nPart, payloadToRectangles(pDocView, rSelection.c_str()));
1312 gtk_widget_queue_draw(GTK_WIDGET(pDocView));
1313 break;
1314 }
1315 case LOK_CALLBACK_VIEW_CURSOR_VISIBLE:
1316 {
1317 std::stringstream aStream(pCallback->m_aPayload);
1318 boost::property_tree::ptree aTree;
1319 boost::property_tree::read_json(aStream, aTree);
1320 int nViewId = aTree.get<int>("viewId");
1321 const std::string& rVisible = aTree.get<std::string>("visible");
1322 priv->m_aViewCursorVisibilities[nViewId] = rVisible == "true";
1323 gtk_widget_queue_draw(GTK_WIDGET(pDocView));
1324 break;
1325 }
1326 break;
1327 case LOK_CALLBACK_CELL_VIEW_CURSOR:
1328 {
1329 std::stringstream aStream(pCallback->m_aPayload);
1330 boost::property_tree::ptree aTree;
1331 boost::property_tree::read_json(aStream, aTree);
1332 int nViewId = aTree.get<int>("viewId");
1333 int nPart = aTree.get<int>("part");
1334 const std::string& rRectangle = aTree.get<std::string>("rectangle");
1335 if (rRectangle != "EMPTY")
1336 priv->m_aCellViewCursors[nViewId] = ViewRectangle(nPart, payloadToRectangle(pDocView, rRectangle.c_str()));
1337 else
1338 {
1339 auto it = priv->m_aCellViewCursors.find(nViewId);
1340 if (it != priv->m_aCellViewCursors.end())
1341 priv->m_aCellViewCursors.erase(it);
1342 }
1343 gtk_widget_queue_draw(GTK_WIDGET(pDocView));
1344 break;
1345 }
1346 case LOK_CALLBACK_VIEW_LOCK:
1347 {
1348 std::stringstream aStream(pCallback->m_aPayload);
1349 boost::property_tree::ptree aTree;
1350 boost::property_tree::read_json(aStream, aTree);
1351 int nViewId = aTree.get<int>("viewId");
1352 int nPart = aTree.get<int>("part");
1353 const std::string& rRectangle = aTree.get<std::string>("rectangle");
1354 if (rRectangle != "EMPTY")
1355 priv->m_aViewLockRectangles[nViewId] = ViewRectangle(nPart, payloadToRectangle(pDocView, rRectangle.c_str()));
1356 else
1357 {
1358 auto it = priv->m_aViewLockRectangles.find(nViewId);
1359 if (it != priv->m_aViewLockRectangles.end())
1360 priv->m_aViewLockRectangles.erase(it);
1361 }
1362 gtk_widget_queue_draw(GTK_WIDGET(pDocView));
1363 break;
1364 }
1365 case LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED:
1366 {
1367 break;
1368 }
1369 case LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED:
1370 {
1371 break;
1372 }
1373 case LOK_CALLBACK_COMMENT:
1374 g_signal_emit(pCallback->m_pDocView, doc_view_signals[COMMENT], 0, pCallback->m_aPayload.c_str());
1375 break;
1376 case LOK_CALLBACK_RULER_UPDATE:
1377 g_signal_emit(pCallback->m_pDocView, doc_view_signals[RULER], 0, pCallback->m_aPayload.c_str());
1378 break;
1379 case LOK_CALLBACK_WINDOW:
1380 g_signal_emit(pCallback->m_pDocView, doc_view_signals[WINDOW], 0, pCallback->m_aPayload.c_str());
1381 break;
1382 case LOK_CALLBACK_INVALIDATE_HEADER:
1383 g_signal_emit(pCallback->m_pDocView, doc_view_signals[INVALIDATE_HEADER], 0, pCallback->m_aPayload.c_str());
1384 break;
1385 case LOK_CALLBACK_REFERENCE_MARKS:
1386 {
1387 std::stringstream aStream(pCallback->m_aPayload);
1388 boost::property_tree::ptree aTree;
1389 boost::property_tree::read_json(aStream, aTree);
1390
1391 priv->m_aReferenceMarks.clear();
1392
1393 for(const auto& rMark : aTree.get_child("marks"))
1394 {
1395 sal_uInt32 nColor = std::stoi(rMark.second.get<std::string>("color"), nullptr, 16);
1396 std::string sRect = rMark.second.get<std::string>("rectangle");
1397 sal_uInt32 nPart = std::stoi(rMark.second.get<std::string>("part"));
1398
1399 GdkRectangle aRect = payloadToRectangle(pDocView, sRect.c_str());
1400 priv->m_aReferenceMarks.push_back(std::pair<ViewRectangle, sal_uInt32>(ViewRectangle(nPart, aRect), nColor));
1401 }
1402
1403 gtk_widget_queue_draw(GTK_WIDGET(pDocView));
1404 break;
1405 }
1406
1407 case LOK_CALLBACK_CONTENT_CONTROL:
1408 {
1409 std::stringstream aPayloadStream(pCallback->m_aPayload);
1410 boost::property_tree::ptree aTree;
1411 boost::property_tree::read_json(aPayloadStream, aTree);
1412 auto aAction = aTree.get<std::string>("action");
1413 if (aAction == "show")
1414 {
1415 auto aRectangles = aTree.get<std::string>("rectangles");
1416 priv->m_aContentControlRectangles = payloadToRectangles(pDocView, aRectangles.c_str());
1417
1418 auto it = aTree.find("alias");
1419 if (it == aTree.not_found())
1420 {
1421 priv->m_aContentControlAlias.clear();
1422 }
1423 else
1424 {
1425 priv->m_aContentControlAlias = it->second.get_value<std::string>();
1426 }
1427 }
1428 else if (aAction == "hide")
1429 {
1430 priv->m_aContentControlRectangles.clear();
1431 priv->m_aContentControlAlias.clear();
1432 }
1433 else if (aAction == "change-picture")
1434 {
1435 GtkWidget* pDialog = gtk_file_chooser_dialog_new(
1436 "Open File", GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(pDocView))),
1437 GTK_FILE_CHOOSER_ACTION_OPEN, "Cancel", GTK_RESPONSE_CANCEL, "Open",
1438 GTK_RESPONSE_ACCEPT, nullptr);
1439 gint nRet = gtk_dialog_run(GTK_DIALOG(pDialog));
1440 if (nRet == GTK_RESPONSE_ACCEPT)
1441 {
1442 GtkFileChooser* pChooser = GTK_FILE_CHOOSER(pDialog);
1443 char* pFilename = gtk_file_chooser_get_uri(pChooser);
1444 boost::property_tree::ptree aValues;
1445 aValues.put("type", "picture");
1446 aValues.put("changed", pFilename);
1447 std::stringstream aStream;
1448 boost::property_tree::write_json(aStream, aValues);
1449 std::string aJson = aStream.str();
1450 lok_doc_view_send_content_control_event(pDocView, aJson.c_str());
1451
1452 g_free(pFilename);
1453 }
1454 gtk_widget_destroy(pDialog);
1455 }
1456 g_signal_emit(pCallback->m_pDocView, doc_view_signals[CONTENT_CONTROL], 0,
1457 pCallback->m_aPayload.c_str());
1458 gtk_widget_queue_draw(GTK_WIDGET(pDocView));
1459 }
1460 break;
1461
1462 case LOK_CALLBACK_STATUS_INDICATOR_START:
1463 case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE:
1464 case LOK_CALLBACK_STATUS_INDICATOR_FINISH:
1465 case LOK_CALLBACK_DOCUMENT_PASSWORD:
1466 case LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY:
1467 case LOK_CALLBACK_VALIDITY_LIST_BUTTON:
1468 case LOK_CALLBACK_VALIDITY_INPUT_HELP:
1469 case LOK_CALLBACK_SIGNATURE_STATUS:
1470 case LOK_CALLBACK_CONTEXT_MENU:
1471 case LOK_CALLBACK_PROFILE_FRAME:
1472 case LOK_CALLBACK_CLIPBOARD_CHANGED:
1473 case LOK_CALLBACK_CONTEXT_CHANGED:
1474 case LOK_CALLBACK_CELL_SELECTION_AREA:
1475 case LOK_CALLBACK_CELL_AUTO_FILL_AREA:
1476 case LOK_CALLBACK_TABLE_SELECTED:
1477 case LOK_CALLBACK_JSDIALOG:
1478 case LOK_CALLBACK_CALC_FUNCTION_LIST:
1479 case LOK_CALLBACK_TAB_STOP_LIST:
1480 case LOK_CALLBACK_FORM_FIELD_BUTTON:
1481 case LOK_CALLBACK_INVALIDATE_SHEET_GEOMETRY:
1482 case LOK_CALLBACK_DOCUMENT_BACKGROUND_COLOR:
1483 case LOK_COMMAND_BLOCKED:
1484 case LOK_CALLBACK_SC_FOLLOW_JUMP:
1485 case LOK_CALLBACK_PRINT_RANGES:
1486 case LOK_CALLBACK_FONTS_MISSING:
1487 case LOK_CALLBACK_MEDIA_SHAPE:
1488 case LOK_CALLBACK_EXPORT_FILE:
1489 case LOK_CALLBACK_VIEW_RENDER_STATE:
1490 case LOK_CALLBACK_APPLICATION_BACKGROUND_COLOR:
1491 case LOK_CALLBACK_A11Y_FOCUS_CHANGED:
1492 case LOK_CALLBACK_A11Y_CARET_CHANGED:
1493 case LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED:
1494 case LOK_CALLBACK_COLOR_PALETTES:
1495 case LOK_CALLBACK_DOCUMENT_PASSWORD_RESET:
1496 {
1497 // TODO: Implement me
1498 break;
1499 }
1500 }
1501 delete pCallback;
1502
1503 return G_SOURCE_REMOVE;
1504}
1505
1506static void callbackWorker (int nType, const char* pPayload, void* pData)
1507{
1508 LOKDocView* pDocView = LOK_DOC_VIEW (pData);
1509
1510 CallbackData* pCallback = new CallbackData(nType, pPayload ? pPayload : "(nil)", pDocView);
1511 LOKDocViewPrivate& priv = getPrivate(pDocView);
1512 std::stringstream ss;
1513 ss << "callbackWorker, view #" << priv->m_nViewId << ": " << lokCallbackTypeToString(nType) << ", '" << (pPayload ? pPayload : "(nil)") << "'";
1514 g_info("%s", ss.str().c_str());
1515 gdk_threads_add_idle(callback, pCallback);
1516}
1517
1518static void
1519renderHandle(LOKDocView* pDocView,
1520 cairo_t* pCairo,
1521 const GdkRectangle& rCursor,
1522 cairo_surface_t* pHandle,
1523 GdkRectangle& rRectangle)
1524{
1525 LOKDocViewPrivate& priv = getPrivate(pDocView);
1526 gint nScaleFactor = gtk_widget_get_scale_factor(GTK_WIDGET(pDocView));
1527 GdkPoint aCursorBottom;
1528 int nHandleWidth, nHandleHeight;
1529 double fHandleScale;
1530
1531 nHandleWidth = cairo_image_surface_get_width(pHandle);
1532 nHandleHeight = cairo_image_surface_get_height(pHandle);
1533 // We want to scale down the handle, so that its height is the same as the cursor caret.
1534 fHandleScale = twipToPixel(rCursor.height, priv->m_fZoom) / nHandleHeight;
1535 // We want the top center of the handle bitmap to be at the bottom center of the cursor rectangle.
1536 aCursorBottom.x = twipToPixel(rCursor.x, priv->m_fZoom) + twipToPixel(rCursor.width, priv->m_fZoom) / 2 - (nHandleWidth * fHandleScale) / 2;
1537 aCursorBottom.y = twipToPixel(rCursor.y, priv->m_fZoom) + twipToPixel(rCursor.height, priv->m_fZoom);
1538
1539 cairo_save (pCairo);
1540 cairo_scale(pCairo, 1.0 / nScaleFactor, 1.0 / nScaleFactor);
1541 cairo_translate(pCairo, aCursorBottom.x * nScaleFactor, aCursorBottom.y * nScaleFactor);
1542 cairo_scale(pCairo, fHandleScale * nScaleFactor, fHandleScale * nScaleFactor);
1543 cairo_set_source_surface(pCairo, pHandle, 0, 0);
1544 cairo_paint(pCairo);
1545 cairo_restore (pCairo);
1546
1547 rRectangle.x = aCursorBottom.x;
1548 rRectangle.y = aCursorBottom.y;
1549 rRectangle.width = nHandleWidth * fHandleScale;
1550 rRectangle.height = nHandleHeight * fHandleScale;
1551}
1552
1554static void
1555renderGraphicHandle(LOKDocView* pDocView,
1556 cairo_t* pCairo,
1557 const GdkRectangle& rSelection,
1558 const GdkRGBA& rColor)
1559{
1560 LOKDocViewPrivate& priv = getPrivate(pDocView);
1561 int nHandleWidth = 9, nHandleHeight = 9;
1562 GdkRectangle aSelection;
1563
1564 aSelection.x = twipToPixel(rSelection.x, priv->m_fZoom);
1565 aSelection.y = twipToPixel(rSelection.y, priv->m_fZoom);
1566 aSelection.width = twipToPixel(rSelection.width, priv->m_fZoom);
1567 aSelection.height = twipToPixel(rSelection.height, priv->m_fZoom);
1568
1569 for (int i = 0; i < GRAPHIC_HANDLE_COUNT; ++i)
1570 {
1571 int x = aSelection.x, y = aSelection.y;
1572
1573 switch (i)
1574 {
1575 case 0: // top-left
1576 break;
1577 case 1: // top-middle
1578 x += aSelection.width / 2;
1579 break;
1580 case 2: // top-right
1581 x += aSelection.width;
1582 break;
1583 case 3: // middle-left
1584 y += aSelection.height / 2;
1585 break;
1586 case 4: // middle-right
1587 x += aSelection.width;
1588 y += aSelection.height / 2;
1589 break;
1590 case 5: // bottom-left
1591 y += aSelection.height;
1592 break;
1593 case 6: // bottom-middle
1594 x += aSelection.width / 2;
1595 y += aSelection.height;
1596 break;
1597 case 7: // bottom-right
1598 x += aSelection.width;
1599 y += aSelection.height;
1600 break;
1601 }
1602
1603 // Center the handle.
1604 x -= nHandleWidth / 2;
1605 y -= nHandleHeight / 2;
1606
1607 priv->m_aGraphicHandleRects[i].x = x;
1608 priv->m_aGraphicHandleRects[i].y = y;
1609 priv->m_aGraphicHandleRects[i].width = nHandleWidth;
1610 priv->m_aGraphicHandleRects[i].height = nHandleHeight;
1611
1612 cairo_set_source_rgb(pCairo, rColor.red, rColor.green, rColor.blue);
1613 cairo_rectangle(pCairo, x, y, nHandleWidth, nHandleHeight);
1614 cairo_fill(pCairo);
1615 }
1616}
1617
1619static gpointer
1620paintTileFinish(LOKDocView* pDocView, GAsyncResult* res, GError **error)
1621{
1622 GTask* task = G_TASK(res);
1623
1624 g_return_val_if_fail(LOK_IS_DOC_VIEW(pDocView), nullptr);
1625 g_return_val_if_fail(g_task_is_valid(res, pDocView), nullptr);
1626 g_return_val_if_fail(error == nullptr || *error == nullptr, nullptr);
1627
1628 return g_task_propagate_pointer(task, error);
1629}
1630
1632static void
1633paintTileCallback(GObject* sourceObject, GAsyncResult* res, gpointer userData)
1634{
1635 LOKDocView* pDocView = LOK_DOC_VIEW(sourceObject);
1636 LOKDocViewPrivate& priv = getPrivate(pDocView);
1637 LOEvent* pLOEvent = static_cast<LOEvent*>(userData);
1638 std::unique_ptr<TileBuffer>& buffer = priv->m_pTileBuffer;
1639 GError* error;
1640
1641 error = nullptr;
1642 cairo_surface_t* pSurface = static_cast<cairo_surface_t*>(paintTileFinish(pDocView, res, &error));
1643 if (error != nullptr)
1644 {
1645 if (error->domain == LOK_TILEBUFFER_ERROR &&
1646 error->code == LOK_TILEBUFFER_CHANGED)
1647 g_info("Skipping paint tile request because corresponding"
1648 "tile buffer has been destroyed");
1649 else
1650 g_warning("Unable to get painted GdkPixbuf: %s", error->message);
1651 g_error_free(error);
1652 return;
1653 }
1654
1655 buffer->setTile(pLOEvent->m_nPaintTileX, pLOEvent->m_nPaintTileY, pSurface);
1656 gdk_threads_add_idle(queueDraw, GTK_WIDGET(pDocView));
1657
1658 cairo_surface_destroy(pSurface);
1659}
1660
1661
1662static bool
1663renderDocument(LOKDocView* pDocView, cairo_t* pCairo)
1664{
1665 LOKDocViewPrivate& priv = getPrivate(pDocView);
1666 GdkRectangle aVisibleArea;
1667 gint nScaleFactor = gtk_widget_get_scale_factor(GTK_WIDGET(pDocView));
1668 gint nTileSizePixelsScaled = nTileSizePixels * nScaleFactor;
1669 long nDocumentWidthPixels = twipToPixel(priv->m_nDocumentWidthTwips, priv->m_fZoom) * nScaleFactor;
1670 long nDocumentHeightPixels = twipToPixel(priv->m_nDocumentHeightTwips, priv->m_fZoom) * nScaleFactor;
1671 // Total number of rows / columns in this document.
1672 guint nRows = ceil(static_cast<double>(nDocumentHeightPixels) / nTileSizePixelsScaled);
1673 guint nColumns = ceil(static_cast<double>(nDocumentWidthPixels) / nTileSizePixelsScaled);
1674
1675 cairo_save (pCairo);
1676 cairo_scale (pCairo, 1.0/nScaleFactor, 1.0/nScaleFactor);
1677 gdk_cairo_get_clip_rectangle (pCairo, &aVisibleArea);
1678 aVisibleArea.x = pixelToTwip (aVisibleArea.x, priv->m_fZoom);
1679 aVisibleArea.y = pixelToTwip (aVisibleArea.y, priv->m_fZoom);
1680 aVisibleArea.width = pixelToTwip (aVisibleArea.width, priv->m_fZoom);
1681 aVisibleArea.height = pixelToTwip (aVisibleArea.height, priv->m_fZoom);
1682
1683 // Render the tiles.
1684 for (guint nRow = 0; nRow < nRows; ++nRow)
1685 {
1686 for (guint nColumn = 0; nColumn < nColumns; ++nColumn)
1687 {
1688 GdkRectangle aTileRectangleTwips, aTileRectanglePixels;
1689 bool bPaint = true;
1690
1691 // Determine size of the tile: the rightmost/bottommost tiles may
1692 // be smaller, and we need the size to decide if we need to repaint.
1693 if (nColumn == nColumns - 1)
1694 aTileRectanglePixels.width = nDocumentWidthPixels - nColumn * nTileSizePixelsScaled;
1695 else
1696 aTileRectanglePixels.width = nTileSizePixelsScaled;
1697 if (nRow == nRows - 1)
1698 aTileRectanglePixels.height = nDocumentHeightPixels - nRow * nTileSizePixelsScaled;
1699 else
1700 aTileRectanglePixels.height = nTileSizePixelsScaled;
1701
1702 // Determine size and position of the tile in document coordinates,
1703 // so we can decide if we can skip painting for partial rendering.
1704 aTileRectangleTwips.x = pixelToTwip(nTileSizePixelsScaled, priv->m_fZoom) * nColumn;
1705 aTileRectangleTwips.y = pixelToTwip(nTileSizePixelsScaled, priv->m_fZoom) * nRow;
1706 aTileRectangleTwips.width = pixelToTwip(aTileRectanglePixels.width, priv->m_fZoom);
1707 aTileRectangleTwips.height = pixelToTwip(aTileRectanglePixels.height, priv->m_fZoom);
1708
1709 if (!gdk_rectangle_intersect(&aVisibleArea, &aTileRectangleTwips, nullptr))
1710 bPaint = false;
1711
1712 if (bPaint)
1713 {
1714 LOEvent* pLOEvent = new LOEvent(LOK_PAINT_TILE);
1715 pLOEvent->m_nPaintTileX = nRow;
1716 pLOEvent->m_nPaintTileY = nColumn;
1717 pLOEvent->m_fPaintTileZoom = priv->m_fZoom;
1718 pLOEvent->m_pTileBuffer = &*priv->m_pTileBuffer;
1719 GTask* task = g_task_new(pDocView, nullptr, paintTileCallback, pLOEvent);
1720 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
1721
1722 Tile& currentTile = priv->m_pTileBuffer->getTile(nRow, nColumn, task, priv->lokThreadPool);
1723 cairo_surface_t* pSurface = currentTile.getBuffer();
1724 cairo_set_source_surface(pCairo, pSurface,
1725 twipToPixel(aTileRectangleTwips.x, priv->m_fZoom),
1726 twipToPixel(aTileRectangleTwips.y, priv->m_fZoom));
1727 cairo_paint(pCairo);
1728 g_object_unref(task);
1729 }
1730 }
1731 }
1732
1733 cairo_restore (pCairo);
1734 return false;
1735}
1736
1737static const GdkRGBA& getDarkColor(int nViewId, LOKDocViewPrivate& priv)
1738{
1739 static std::map<int, GdkRGBA> aColorMap;
1740 auto it = aColorMap.find(nViewId);
1741 if (it != aColorMap.end())
1742 return it->second;
1743
1744 if (priv->m_eDocumentType == LOK_DOCTYPE_TEXT)
1745 {
1746 char* pValues = priv->m_pDocument->pClass->getCommandValues(priv->m_pDocument, ".uno:TrackedChangeAuthors");
1747 std::stringstream aInfo;
1748 aInfo << "lok::Document::getCommandValues('.uno:TrackedChangeAuthors') returned '" << pValues << "'" << std::endl;
1749 g_info("%s", aInfo.str().c_str());
1750
1751 std::stringstream aStream(pValues);
1752 boost::property_tree::ptree aTree;
1753 boost::property_tree::read_json(aStream, aTree);
1754 for (const auto& rValue : aTree.get_child("authors"))
1755 {
1756 const std::string& rName = rValue.second.get<std::string>("name");
1757 guint32 nColor = rValue.second.get<guint32>("color");
1758 GdkRGBA aColor{static_cast<double>(static_cast<guint8>(nColor>>16))/255, static_cast<double>(static_cast<guint8>(static_cast<guint16>(nColor) >> 8))/255, static_cast<double>(static_cast<guint8>(nColor))/255, 0};
1759 auto itAuthorViews = g_aAuthorViews.find(rName);
1760 if (itAuthorViews != g_aAuthorViews.end())
1761 aColorMap[itAuthorViews->second] = aColor;
1762 }
1763 }
1764 else
1765 {
1766 // Based on tools/color.hxx, COL_AUTHOR1_DARK..COL_AUTHOR9_DARK.
1767 static std::vector<GdkRGBA> aColors =
1768 {
1769 {(double(198))/255, (double(146))/255, (double(0))/255, 0},
1770 {(double(6))/255, (double(70))/255, (double(162))/255, 0},
1771 {(double(87))/255, (double(157))/255, (double(28))/255, 0},
1772 {(double(105))/255, (double(43))/255, (double(157))/255, 0},
1773 {(double(197))/255, (double(0))/255, (double(11))/255, 0},
1774 {(double(0))/255, (double(128))/255, (double(128))/255, 0},
1775 {(double(140))/255, (double(132))/255, (double(0))/255, 0},
1776 {(double(43))/255, (double(85))/255, (double(107))/255, 0},
1777 {(double(209))/255, (double(118))/255, (double(0))/255, 0},
1778 };
1779 static int nColorCounter = 0;
1780 GdkRGBA aColor = aColors[nColorCounter++ % aColors.size()];
1781 aColorMap[nViewId] = aColor;
1782 }
1783 assert(aColorMap.find(nViewId) != aColorMap.end());
1784 return aColorMap[nViewId];
1785}
1786
1787static bool
1788renderOverlay(LOKDocView* pDocView, cairo_t* pCairo)
1789{
1790 LOKDocViewPrivate& priv = getPrivate(pDocView);
1791
1792 if (priv->m_bEdit && priv->m_bCursorVisible && priv->m_bCursorOverlayVisible && !isEmptyRectangle(priv->m_aVisibleCursor))
1793 {
1794 if (priv->m_aVisibleCursor.width < 30)
1795 // Set a minimal width if it would be 0.
1796 priv->m_aVisibleCursor.width = 30;
1797
1798 cairo_set_source_rgb(pCairo, 0, 0, 0);
1799 cairo_rectangle(pCairo,
1800 twipToPixel(priv->m_aVisibleCursor.x, priv->m_fZoom),
1801 twipToPixel(priv->m_aVisibleCursor.y, priv->m_fZoom),
1802 twipToPixel(priv->m_aVisibleCursor.width, priv->m_fZoom),
1803 twipToPixel(priv->m_aVisibleCursor.height, priv->m_fZoom));
1804 cairo_fill(pCairo);
1805 }
1806
1807 // View cursors: they do not blink and are colored.
1808 if (priv->m_bEdit && !priv->m_aViewCursors.empty())
1809 {
1810 for (auto& rPair : priv->m_aViewCursors)
1811 {
1812 auto itVisibility = priv->m_aViewCursorVisibilities.find(rPair.first);
1813 if (itVisibility != priv->m_aViewCursorVisibilities.end() && !itVisibility->second)
1814 continue;
1815
1816 // Show view cursors when in Writer or when the part matches.
1817 if (rPair.second.m_nPart != priv->m_nPartId && priv->m_eDocumentType != LOK_DOCTYPE_TEXT)
1818 continue;
1819
1820 GdkRectangle& rCursor = rPair.second.m_aRectangle;
1821 if (rCursor.width < 30)
1822 // Set a minimal width if it would be 0.
1823 rCursor.width = 30;
1824
1825 const GdkRGBA& rDark = getDarkColor(rPair.first, priv);
1826 cairo_set_source_rgb(pCairo, rDark.red, rDark.green, rDark.blue);
1827 cairo_rectangle(pCairo,
1828 twipToPixel(rCursor.x, priv->m_fZoom),
1829 twipToPixel(rCursor.y, priv->m_fZoom),
1830 twipToPixel(rCursor.width, priv->m_fZoom),
1831 twipToPixel(rCursor.height, priv->m_fZoom));
1832 cairo_fill(pCairo);
1833 }
1834 }
1835
1836 if (priv->m_bEdit && priv->m_bCursorVisible && !isEmptyRectangle(priv->m_aVisibleCursor) && priv->m_aTextSelectionRectangles.empty())
1837 {
1838 // Have a cursor, but no selection: we need the middle handle.
1839 gchar* handleMiddlePath = g_strconcat (priv->m_aLOPath.c_str(), CURSOR_HANDLE_DIR, "handle_image_middle.png", nullptr);
1840 if (!priv->m_pHandleMiddle)
1841 {
1842 priv->m_pHandleMiddle = cairo_image_surface_create_from_png(handleMiddlePath);
1843 assert(cairo_surface_status(priv->m_pHandleMiddle) == CAIRO_STATUS_SUCCESS);
1844 }
1845 g_free (handleMiddlePath);
1846 renderHandle(pDocView, pCairo, priv->m_aVisibleCursor, priv->m_pHandleMiddle, priv->m_aHandleMiddleRect);
1847 }
1848
1849 if (!priv->m_aTextSelectionRectangles.empty())
1850 {
1851 for (const GdkRectangle& rRectangle : priv->m_aTextSelectionRectangles)
1852 {
1853 // Blue with 75% transparency.
1854 cairo_set_source_rgba(pCairo, (double(0x43))/255, (double(0xac))/255, (double(0xe8))/255, 0.25);
1855 cairo_rectangle(pCairo,
1856 twipToPixel(rRectangle.x, priv->m_fZoom),
1857 twipToPixel(rRectangle.y, priv->m_fZoom),
1858 twipToPixel(rRectangle.width, priv->m_fZoom),
1859 twipToPixel(rRectangle.height, priv->m_fZoom));
1860 cairo_fill(pCairo);
1861 }
1862
1863 // Handles
1864 if (!isEmptyRectangle(priv->m_aTextSelectionStart))
1865 {
1866 // Have a start position: we need a start handle.
1867 gchar* handleStartPath = g_strconcat (priv->m_aLOPath.c_str(), CURSOR_HANDLE_DIR, "handle_image_start.png", nullptr);
1868 if (!priv->m_pHandleStart)
1869 {
1870 priv->m_pHandleStart = cairo_image_surface_create_from_png(handleStartPath);
1871 assert(cairo_surface_status(priv->m_pHandleStart) == CAIRO_STATUS_SUCCESS);
1872 }
1873 renderHandle(pDocView, pCairo, priv->m_aTextSelectionStart, priv->m_pHandleStart, priv->m_aHandleStartRect);
1874 g_free (handleStartPath);
1875 }
1876 if (!isEmptyRectangle(priv->m_aTextSelectionEnd))
1877 {
1878 // Have a start position: we need an end handle.
1879 gchar* handleEndPath = g_strconcat (priv->m_aLOPath.c_str(), CURSOR_HANDLE_DIR, "handle_image_end.png", nullptr);
1880 if (!priv->m_pHandleEnd)
1881 {
1882 priv->m_pHandleEnd = cairo_image_surface_create_from_png(handleEndPath);
1883 assert(cairo_surface_status(priv->m_pHandleEnd) == CAIRO_STATUS_SUCCESS);
1884 }
1885 renderHandle(pDocView, pCairo, priv->m_aTextSelectionEnd, priv->m_pHandleEnd, priv->m_aHandleEndRect);
1886 g_free (handleEndPath);
1887 }
1888 }
1889
1890 if (!priv->m_aContentControlRectangles.empty())
1891 {
1892 for (const GdkRectangle& rRectangle : priv->m_aContentControlRectangles)
1893 {
1894 // Black with 75% transparency.
1895 cairo_set_source_rgba(pCairo, (double(0x7f))/255, (double(0x7f))/255, (double(0x7f))/255, 0.25);
1896 cairo_rectangle(pCairo,
1897 twipToPixel(rRectangle.x, priv->m_fZoom),
1898 twipToPixel(rRectangle.y, priv->m_fZoom),
1899 twipToPixel(rRectangle.width, priv->m_fZoom),
1900 twipToPixel(rRectangle.height, priv->m_fZoom));
1901 cairo_fill(pCairo);
1902 }
1903
1904 if (!priv->m_aContentControlAlias.empty())
1905 {
1906 cairo_text_extents_t aExtents;
1907 cairo_text_extents(pCairo, priv->m_aContentControlAlias.c_str(), &aExtents);
1908 // Blue with 75% transparency.
1909 cairo_set_source_rgba(pCairo, 0, 0, 1, 0.25);
1910 cairo_rectangle(pCairo,
1911 twipToPixel(priv->m_aContentControlRectangles[0].x, priv->m_fZoom) + aExtents.x_bearing,
1912 twipToPixel(priv->m_aContentControlRectangles[0].y, priv->m_fZoom) + aExtents.y_bearing,
1913 aExtents.width,
1914 aExtents.height);
1915 cairo_fill(pCairo);
1916
1917 cairo_move_to(pCairo,
1918 twipToPixel(priv->m_aContentControlRectangles[0].x, priv->m_fZoom),
1919 twipToPixel(priv->m_aContentControlRectangles[0].y, priv->m_fZoom));
1920 cairo_set_source_rgb(pCairo, 0, 0, 0);
1921 cairo_show_text(pCairo, priv->m_aContentControlAlias.c_str());
1922 cairo_fill(pCairo);
1923 }
1924 }
1925
1926 // Selections of other views.
1927 for (const auto& rPair : priv->m_aTextViewSelectionRectangles)
1928 {
1929 if (rPair.second.m_nPart != priv->m_nPartId && priv->m_eDocumentType != LOK_DOCTYPE_TEXT)
1930 continue;
1931
1932 for (const GdkRectangle& rRectangle : rPair.second.m_aRectangles)
1933 {
1934 const GdkRGBA& rDark = getDarkColor(rPair.first, priv);
1935 // 75% transparency.
1936 cairo_set_source_rgba(pCairo, rDark.red, rDark.green, rDark.blue, 0.25);
1937 cairo_rectangle(pCairo,
1938 twipToPixel(rRectangle.x, priv->m_fZoom),
1939 twipToPixel(rRectangle.y, priv->m_fZoom),
1940 twipToPixel(rRectangle.width, priv->m_fZoom),
1941 twipToPixel(rRectangle.height, priv->m_fZoom));
1942 cairo_fill(pCairo);
1943 }
1944 }
1945
1946 if (!isEmptyRectangle(priv->m_aGraphicSelection))
1947 {
1948 GdkRGBA const aBlack{0, 0, 0, 0};
1949 renderGraphicHandle(pDocView, pCairo, priv->m_aGraphicSelection, aBlack);
1950 }
1951
1952 // Graphic selections of other views.
1953 for (const auto& rPair : priv->m_aGraphicViewSelections)
1954 {
1955 const ViewRectangle& rRectangle = rPair.second;
1956 if (rRectangle.m_nPart != priv->m_nPartId && priv->m_eDocumentType != LOK_DOCTYPE_TEXT)
1957 continue;
1958
1959 const GdkRGBA& rDark = getDarkColor(rPair.first, priv);
1960 renderGraphicHandle(pDocView, pCairo, rRectangle.m_aRectangle, rDark);
1961 }
1962
1963 // Draw the cell cursor.
1964 if (!isEmptyRectangle(priv->m_aCellCursor))
1965 {
1966 cairo_set_source_rgb(pCairo, 0, 0, 0);
1967 cairo_rectangle(pCairo,
1968 twipToPixel(priv->m_aCellCursor.x, priv->m_fZoom),
1969 twipToPixel(priv->m_aCellCursor.y, priv->m_fZoom),
1970 twipToPixel(priv->m_aCellCursor.width, priv->m_fZoom),
1971 twipToPixel(priv->m_aCellCursor.height, priv->m_fZoom));
1972 cairo_set_line_width(pCairo, 2.0);
1973 cairo_stroke(pCairo);
1974 }
1975
1976 // Cell view cursors: they are colored.
1977 for (const auto& rPair : priv->m_aCellViewCursors)
1978 {
1979 const ViewRectangle& rCursor = rPair.second;
1980 if (rCursor.m_nPart != priv->m_nPartId)
1981 continue;
1982
1983 const GdkRGBA& rDark = getDarkColor(rPair.first, priv);
1984 cairo_set_source_rgb(pCairo, rDark.red, rDark.green, rDark.blue);
1985 cairo_rectangle(pCairo,
1986 twipToPixel(rCursor.m_aRectangle.x, priv->m_fZoom),
1987 twipToPixel(rCursor.m_aRectangle.y, priv->m_fZoom),
1988 twipToPixel(rCursor.m_aRectangle.width, priv->m_fZoom),
1989 twipToPixel(rCursor.m_aRectangle.height, priv->m_fZoom));
1990 cairo_set_line_width(pCairo, 2.0);
1991 cairo_stroke(pCairo);
1992 }
1993
1994 // Draw reference marks.
1995 for (const auto& rPair : priv->m_aReferenceMarks)
1996 {
1997 const ViewRectangle& rMark = rPair.first;
1998 if (rMark.m_nPart != priv->m_nPartId)
1999 continue;
2000
2001 sal_uInt32 nColor = rPair.second;
2002 sal_uInt8 nRed = (nColor >> 16) & 0xff;
2003 sal_uInt8 nGreen = (nColor >> 8) & 0xff;
2004 sal_uInt8 nBlue = nColor & 0xff;
2005 cairo_set_source_rgb(pCairo, nRed, nGreen, nBlue);
2006 cairo_rectangle(pCairo,
2007 twipToPixel(rMark.m_aRectangle.x, priv->m_fZoom),
2008 twipToPixel(rMark.m_aRectangle.y, priv->m_fZoom),
2009 twipToPixel(rMark.m_aRectangle.width, priv->m_fZoom),
2010 twipToPixel(rMark.m_aRectangle.height, priv->m_fZoom));
2011 cairo_set_line_width(pCairo, 2.0);
2012 cairo_stroke(pCairo);
2013 }
2014
2015 // View locks: they are colored.
2016 for (const auto& rPair : priv->m_aViewLockRectangles)
2017 {
2018 const ViewRectangle& rRectangle = rPair.second;
2019 if (rRectangle.m_nPart != priv->m_nPartId)
2020 continue;
2021
2022 // Draw a rectangle.
2023 const GdkRGBA& rDark = getDarkColor(rPair.first, priv);
2024 cairo_set_source_rgb(pCairo, rDark.red, rDark.green, rDark.blue);
2025 cairo_rectangle(pCairo,
2026 twipToPixel(rRectangle.m_aRectangle.x, priv->m_fZoom),
2027 twipToPixel(rRectangle.m_aRectangle.y, priv->m_fZoom),
2028 twipToPixel(rRectangle.m_aRectangle.width, priv->m_fZoom),
2029 twipToPixel(rRectangle.m_aRectangle.height, priv->m_fZoom));
2030 cairo_set_line_width(pCairo, 2.0);
2031 cairo_stroke(pCairo);
2032
2033 // And a lock.
2034 cairo_rectangle(pCairo,
2035 twipToPixel(rRectangle.m_aRectangle.x + rRectangle.m_aRectangle.width, priv->m_fZoom) - 25,
2036 twipToPixel(rRectangle.m_aRectangle.y + rRectangle.m_aRectangle.height, priv->m_fZoom) - 15,
2037 20,
2038 10);
2039 cairo_fill(pCairo);
2040 cairo_arc(pCairo,
2041 twipToPixel(rRectangle.m_aRectangle.x + rRectangle.m_aRectangle.width, priv->m_fZoom) - 15,
2042 twipToPixel(rRectangle.m_aRectangle.y + rRectangle.m_aRectangle.height, priv->m_fZoom) - 15,
2043 5,
2044 M_PI,
2045 2 * M_PI);
2046 cairo_stroke(pCairo);
2047 }
2048
2049 return false;
2050}
2051
2052static gboolean
2053lok_doc_view_signal_button(GtkWidget* pWidget, GdkEventButton* pEvent)
2054{
2055 LOKDocView* pDocView = LOK_DOC_VIEW (pWidget);
2056 LOKDocViewPrivate& priv = getPrivate(pDocView);
2057 GError* error = nullptr;
2058
2059 g_info("LOKDocView_Impl::signalButton: %d, %d (in twips: %d, %d)",
2060 static_cast<int>(pEvent->x), static_cast<int>(pEvent->y),
2061 static_cast<int>(pixelToTwip(pEvent->x, priv->m_fZoom)),
2062 static_cast<int>(pixelToTwip(pEvent->y, priv->m_fZoom)));
2063 gtk_widget_grab_focus(GTK_WIDGET(pDocView));
2064
2065 switch (pEvent->type)
2066 {
2067 case GDK_BUTTON_PRESS:
2068 {
2069 GdkRectangle aClick;
2070 aClick.x = pEvent->x;
2071 aClick.y = pEvent->y;
2072 aClick.width = 1;
2073 aClick.height = 1;
2074
2075 if (handleTextSelectionOnButtonPress(aClick, pDocView))
2076 return FALSE;
2077 if (handleGraphicSelectionOnButtonPress(aClick, pDocView))
2078 return FALSE;
2079
2080 int nCount = 1;
2081 if ((pEvent->time - priv->m_nLastButtonPressTime) < 250)
2082 nCount++;
2083 priv->m_nLastButtonPressTime = pEvent->time;
2084 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
2085 LOEvent* pLOEvent = new LOEvent(LOK_POST_MOUSE_EVENT);
2086 pLOEvent->m_nPostMouseEventType = LOK_MOUSEEVENT_MOUSEBUTTONDOWN;
2087 pLOEvent->m_nPostMouseEventX = pixelToTwip(pEvent->x, priv->m_fZoom);
2088 pLOEvent->m_nPostMouseEventY = pixelToTwip(pEvent->y, priv->m_fZoom);
2089 pLOEvent->m_nPostMouseEventCount = nCount;
2090 switch (pEvent->button)
2091 {
2092 case 1:
2094 break;
2095 case 2:
2097 break;
2098 case 3:
2100 break;
2101 }
2102 pLOEvent->m_nPostMouseEventModifier = priv->m_nKeyModifier;
2103 priv->m_nLastButtonPressed = pLOEvent->m_nPostMouseEventButton;
2104 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
2105
2106 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
2107 if (error != nullptr)
2108 {
2109 g_warning("Unable to call LOK_POST_MOUSE_EVENT: %s", error->message);
2110 g_clear_error(&error);
2111 }
2112 g_object_unref(task);
2113 break;
2114 }
2115 case GDK_BUTTON_RELEASE:
2116 {
2118 return FALSE;
2119 if (handleGraphicSelectionOnButtonRelease(pDocView, pEvent))
2120 return FALSE;
2121
2122 int nCount = 1;
2123 if ((pEvent->time - priv->m_nLastButtonReleaseTime) < 250)
2124 nCount++;
2125 priv->m_nLastButtonReleaseTime = pEvent->time;
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_MOUSEBUTTONUP;
2129 pLOEvent->m_nPostMouseEventX = pixelToTwip(pEvent->x, priv->m_fZoom);
2130 pLOEvent->m_nPostMouseEventY = pixelToTwip(pEvent->y, priv->m_fZoom);
2131 pLOEvent->m_nPostMouseEventCount = nCount;
2132 switch (pEvent->button)
2133 {
2134 case 1:
2136 break;
2137 case 2:
2139 break;
2140 case 3:
2142 break;
2143 }
2144 pLOEvent->m_nPostMouseEventModifier = priv->m_nKeyModifier;
2145 priv->m_nLastButtonPressed = pLOEvent->m_nPostMouseEventButton;
2146 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
2147
2148 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
2149 if (error != nullptr)
2150 {
2151 g_warning("Unable to call LOK_POST_MOUSE_EVENT: %s", error->message);
2152 g_clear_error(&error);
2153 }
2154 g_object_unref(task);
2155 break;
2156 }
2157 default:
2158 break;
2159 }
2160 return FALSE;
2161}
2162
2163static void
2164getDragPoint(GdkRectangle* pHandle,
2165 GdkEventMotion* pEvent,
2166 GdkPoint* pPoint)
2167{
2168 GdkPoint aCursor, aHandle;
2169
2170 // Center of the cursor rectangle: we know that it's above the handle.
2171 aCursor.x = pHandle->x + pHandle->width / 2;
2172 aCursor.y = pHandle->y - pHandle->height / 2;
2173 // Center of the handle rectangle.
2174 aHandle.x = pHandle->x + pHandle->width / 2;
2175 aHandle.y = pHandle->y + pHandle->height / 2;
2176 // Our target is the original cursor position + the dragged offset.
2177 pPoint->x = aCursor.x + (pEvent->x - aHandle.x);
2178 pPoint->y = aCursor.y + (pEvent->y - aHandle.y);
2179}
2180
2181static gboolean
2182lok_doc_view_signal_motion (GtkWidget* pWidget, GdkEventMotion* pEvent)
2183{
2184 LOKDocView* pDocView = LOK_DOC_VIEW (pWidget);
2185 LOKDocViewPrivate& priv = getPrivate(pDocView);
2186 GdkPoint aPoint;
2187 GError* error = nullptr;
2188
2189 std::unique_lock<std::mutex> aGuard(g_aLOKMutex);
2190 setDocumentView(priv->m_pDocument, priv->m_nViewId);
2191 if (priv->m_bInDragMiddleHandle)
2192 {
2193 g_info("lcl_signalMotion: dragging the middle handle");
2194 getDragPoint(&priv->m_aHandleMiddleRect, pEvent, &aPoint);
2195 priv->m_pDocument->pClass->setTextSelection(priv->m_pDocument, LOK_SETTEXTSELECTION_RESET, pixelToTwip(aPoint.x, priv->m_fZoom), pixelToTwip(aPoint.y, priv->m_fZoom));
2196 return FALSE;
2197 }
2198 if (priv->m_bInDragStartHandle)
2199 {
2200 g_info("lcl_signalMotion: dragging the start handle");
2201 getDragPoint(&priv->m_aHandleStartRect, pEvent, &aPoint);
2202 priv->m_pDocument->pClass->setTextSelection(priv->m_pDocument, LOK_SETTEXTSELECTION_START, pixelToTwip(aPoint.x, priv->m_fZoom), pixelToTwip(aPoint.y, priv->m_fZoom));
2203 return FALSE;
2204 }
2205 if (priv->m_bInDragEndHandle)
2206 {
2207 g_info("lcl_signalMotion: dragging the end handle");
2208 getDragPoint(&priv->m_aHandleEndRect, pEvent, &aPoint);
2209 priv->m_pDocument->pClass->setTextSelection(priv->m_pDocument, LOK_SETTEXTSELECTION_END, pixelToTwip(aPoint.x, priv->m_fZoom), pixelToTwip(aPoint.y, priv->m_fZoom));
2210 return FALSE;
2211 }
2212 aGuard.unlock();
2213 for (int i = 0; i < GRAPHIC_HANDLE_COUNT; ++i)
2214 {
2215 if (priv->m_bInDragGraphicHandles[i])
2216 {
2217 g_info("lcl_signalMotion: dragging the graphic handle #%d", i);
2218 return FALSE;
2219 }
2220 }
2221 if (priv->m_bInDragGraphicSelection)
2222 {
2223 g_info("lcl_signalMotion: dragging the graphic selection");
2224 return FALSE;
2225 }
2226
2227 GdkRectangle aMotionInTwipsInTwips;
2228 aMotionInTwipsInTwips.x = pixelToTwip(pEvent->x, priv->m_fZoom);
2229 aMotionInTwipsInTwips.y = pixelToTwip(pEvent->y, priv->m_fZoom);
2230 aMotionInTwipsInTwips.width = 1;
2231 aMotionInTwipsInTwips.height = 1;
2232 if (gdk_rectangle_intersect(&aMotionInTwipsInTwips, &priv->m_aGraphicSelection, nullptr))
2233 {
2234 g_info("lcl_signalMotion: start of drag graphic selection");
2235 priv->m_bInDragGraphicSelection = true;
2236
2237 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
2238 LOEvent* pLOEvent = new LOEvent(LOK_SET_GRAPHIC_SELECTION);
2239 pLOEvent->m_nSetGraphicSelectionType = LOK_SETGRAPHICSELECTION_START;
2240 pLOEvent->m_nSetGraphicSelectionX = pixelToTwip(pEvent->x, priv->m_fZoom);
2241 pLOEvent->m_nSetGraphicSelectionY = pixelToTwip(pEvent->y, priv->m_fZoom);
2242 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
2243
2244 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
2245 if (error != nullptr)
2246 {
2247 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error->message);
2248 g_clear_error(&error);
2249 }
2250 g_object_unref(task);
2251
2252 return FALSE;
2253 }
2254
2255 // Otherwise a mouse move, as on the desktop.
2256
2257 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
2258 LOEvent* pLOEvent = new LOEvent(LOK_POST_MOUSE_EVENT);
2259 pLOEvent->m_nPostMouseEventType = LOK_MOUSEEVENT_MOUSEMOVE;
2260 pLOEvent->m_nPostMouseEventX = pixelToTwip(pEvent->x, priv->m_fZoom);
2261 pLOEvent->m_nPostMouseEventY = pixelToTwip(pEvent->y, priv->m_fZoom);
2262 pLOEvent->m_nPostMouseEventCount = 1;
2263 pLOEvent->m_nPostMouseEventButton = priv->m_nLastButtonPressed;
2264 pLOEvent->m_nPostMouseEventModifier = priv->m_nKeyModifier;
2265
2266 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
2267
2268 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
2269 if (error != nullptr)
2270 {
2271 g_warning("Unable to call LOK_MOUSEEVENT_MOUSEMOVE: %s", error->message);
2272 g_clear_error(&error);
2273 }
2274 g_object_unref(task);
2275
2276 return FALSE;
2277}
2278
2279static void
2281{
2282 GTask* task = G_TASK(data);
2283 LOKDocView* pDocView = LOK_DOC_VIEW(g_task_get_source_object(task));
2284 LOKDocViewPrivate& priv = getPrivate(pDocView);
2285 LOEvent* pLOEvent = static_cast<LOEvent*>(g_task_get_task_data(task));
2286
2287 std::scoped_lock<std::mutex> aGuard(g_aLOKMutex);
2288 setDocumentView(priv->m_pDocument, priv->m_nViewId);
2289 std::stringstream ss;
2290 ss << "lok::Document::setGraphicSelection(" << pLOEvent->m_nSetGraphicSelectionType;
2291 ss << ", " << pLOEvent->m_nSetGraphicSelectionX;
2292 ss << ", " << pLOEvent->m_nSetGraphicSelectionY << ")";
2293 g_info("%s", ss.str().c_str());
2294 priv->m_pDocument->pClass->setGraphicSelection(priv->m_pDocument,
2296 pLOEvent->m_nSetGraphicSelectionX,
2297 pLOEvent->m_nSetGraphicSelectionY);
2298}
2299
2300static void
2302{
2303 GTask* task = G_TASK(data);
2304 LOKDocView* pDocView = LOK_DOC_VIEW(g_task_get_source_object(task));
2305 LOKDocViewPrivate& priv = getPrivate(pDocView);
2306 LOEvent* pLOEvent = static_cast<LOEvent*>(g_task_get_task_data(task));
2307
2308 std::scoped_lock<std::mutex> aGuard(g_aLOKMutex);
2309 setDocumentView(priv->m_pDocument, priv->m_nViewId);
2310 priv->m_pDocument->pClass->setClientZoom(priv->m_pDocument,
2311 pLOEvent->m_nTilePixelWidth,
2312 pLOEvent->m_nTilePixelHeight,
2313 pLOEvent->m_nTileTwipWidth,
2314 pLOEvent->m_nTileTwipHeight);
2315}
2316
2317static void
2319{
2320 GTask* task = G_TASK(data);
2321 LOKDocView* pDocView = LOK_DOC_VIEW(g_task_get_source_object(task));
2322 LOKDocViewPrivate& priv = getPrivate(pDocView);
2323 LOEvent* pLOEvent = static_cast<LOEvent*>(g_task_get_task_data(task));
2324
2325 std::scoped_lock<std::mutex> aGuard(g_aLOKMutex);
2326 setDocumentView(priv->m_pDocument, priv->m_nViewId);
2327 std::stringstream ss;
2328 ss << "lok::Document::postMouseEvent(" << pLOEvent->m_nPostMouseEventType;
2329 ss << ", " << pLOEvent->m_nPostMouseEventX;
2330 ss << ", " << pLOEvent->m_nPostMouseEventY;
2331 ss << ", " << pLOEvent->m_nPostMouseEventCount;
2332 ss << ", " << pLOEvent->m_nPostMouseEventButton;
2333 ss << ", " << pLOEvent->m_nPostMouseEventModifier << ")";
2334 g_info("%s", ss.str().c_str());
2335 priv->m_pDocument->pClass->postMouseEvent(priv->m_pDocument,
2336 pLOEvent->m_nPostMouseEventType,
2337 pLOEvent->m_nPostMouseEventX,
2338 pLOEvent->m_nPostMouseEventY,
2339 pLOEvent->m_nPostMouseEventCount,
2340 pLOEvent->m_nPostMouseEventButton,
2341 pLOEvent->m_nPostMouseEventModifier);
2342}
2343
2344static void
2346{
2347 GTask* task = G_TASK(data);
2348 LOKDocView* pDocView = LOK_DOC_VIEW(g_task_get_source_object(task));
2349 LOKDocViewPrivate& priv = getPrivate(pDocView);
2350
2351 std::scoped_lock<std::mutex> aGuard(g_aLOKMutex);
2352 if ( priv->m_pDocument )
2353 {
2354 priv->m_pDocument->pClass->destroy( priv->m_pDocument );
2355 priv->m_pDocument = nullptr;
2356 }
2357
2358 priv->m_pOffice->pClass->registerCallback(priv->m_pOffice, globalCallbackWorker, pDocView);
2359 std::string url = priv->m_aDocPath;
2360 if (gchar* pURL = g_filename_to_uri(url.c_str(), nullptr, nullptr))
2361 {
2362 url = pURL;
2363 g_free(pURL);
2364 }
2365 priv->m_pDocument = priv->m_pOffice->pClass->documentLoadWithOptions( priv->m_pOffice, url.c_str(), "en-US" );
2366 if ( !priv->m_pDocument )
2367 {
2368 char *pError = priv->m_pOffice->pClass->getError( priv->m_pOffice );
2369 g_task_return_new_error(task, g_quark_from_static_string ("LOK error"), 0, "%s", pError);
2370 }
2371 else
2372 {
2373 priv->m_eDocumentType = static_cast<LibreOfficeKitDocumentType>(priv->m_pDocument->pClass->getDocumentType(priv->m_pDocument));
2374 gdk_threads_add_idle(postDocumentLoad, pDocView);
2375 g_task_return_boolean (task, true);
2376 }
2377}
2378
2379static void
2380setPartInThread(gpointer data)
2381{
2382 GTask* task = G_TASK(data);
2383 LOKDocView* pDocView = LOK_DOC_VIEW(g_task_get_source_object(task));
2384 LOKDocViewPrivate& priv = getPrivate(pDocView);
2385 LOEvent* pLOEvent = static_cast<LOEvent*>(g_task_get_task_data(task));
2386 int nPart = pLOEvent->m_nPart;
2387
2388 std::unique_lock<std::mutex> aGuard(g_aLOKMutex);
2389 setDocumentView(priv->m_pDocument, priv->m_nViewId);
2390 priv->m_pDocument->pClass->setPart( priv->m_pDocument, nPart );
2391 aGuard.unlock();
2392
2393 lok_doc_view_reset_view(pDocView);
2394}
2395
2396static void
2398{
2399 GTask* task = G_TASK(data);
2400 LOKDocView* pDocView = LOK_DOC_VIEW(g_task_get_source_object(task));
2401 LOKDocViewPrivate& priv = getPrivate(pDocView);
2402 LOEvent* pLOEvent = static_cast<LOEvent*>(g_task_get_task_data(task));
2403 int nPartMode = pLOEvent->m_nPartMode;
2404
2405 std::scoped_lock<std::mutex> aGuard(g_aLOKMutex);
2406 setDocumentView(priv->m_pDocument, priv->m_nViewId);
2407 priv->m_pDocument->pClass->setPartMode( priv->m_pDocument, nPartMode );
2408}
2409
2410static void
2411setEditInThread(gpointer data)
2412{
2413 GTask* task = G_TASK(data);
2414 LOKDocView* pDocView = LOK_DOC_VIEW(g_task_get_source_object(task));
2415 LOKDocViewPrivate& priv = getPrivate(pDocView);
2416 LOEvent* pLOEvent = static_cast<LOEvent*>(g_task_get_task_data(task));
2417 bool bWasEdit = priv->m_bEdit;
2418 bool bEdit = pLOEvent->m_bEdit;
2419
2420 if (!priv->m_bEdit && bEdit)
2421 g_info("lok_doc_view_set_edit: entering edit mode");
2422 else if (priv->m_bEdit && !bEdit)
2423 {
2424 g_info("lok_doc_view_set_edit: leaving edit mode");
2425 std::scoped_lock<std::mutex> aGuard(g_aLOKMutex);
2426 setDocumentView(priv->m_pDocument, priv->m_nViewId);
2427 priv->m_pDocument->pClass->resetSelection(priv->m_pDocument);
2428 }
2429 priv->m_bEdit = bEdit;
2430 g_signal_emit(pDocView, doc_view_signals[EDIT_CHANGED], 0, bWasEdit);
2431 gdk_threads_add_idle(queueDraw, GTK_WIDGET(pDocView));
2432}
2433
2434static void
2435postCommandInThread (gpointer data)
2436{
2437 GTask* task = G_TASK(data);
2438 LOKDocView* pDocView = LOK_DOC_VIEW(g_task_get_source_object(task));
2439 LOEvent* pLOEvent = static_cast<LOEvent*>(g_task_get_task_data(task));
2440 LOKDocViewPrivate& priv = getPrivate(pDocView);
2441
2442 std::scoped_lock<std::mutex> aGuard(g_aLOKMutex);
2443 setDocumentView(priv->m_pDocument, priv->m_nViewId);
2444 std::stringstream ss;
2445 ss << "lok::Document::postUnoCommand(" << pLOEvent->m_pCommand << ", " << pLOEvent->m_pArguments << ")";
2446 g_info("%s", ss.str().c_str());
2447 priv->m_pDocument->pClass->postUnoCommand(priv->m_pDocument, pLOEvent->m_pCommand, pLOEvent->m_pArguments, pLOEvent->m_bNotifyWhenFinished);
2448}
2449
2450static void
2451paintTileInThread (gpointer data)
2452{
2453 GTask* task = G_TASK(data);
2454 LOKDocView* pDocView = LOK_DOC_VIEW(g_task_get_source_object(task));
2455 LOKDocViewPrivate& priv = getPrivate(pDocView);
2456 LOEvent* pLOEvent = static_cast<LOEvent*>(g_task_get_task_data(task));
2457 gint nScaleFactor = gtk_widget_get_scale_factor(GTK_WIDGET(pDocView));
2458 gint nTileSizePixelsScaled = nTileSizePixels * nScaleFactor;
2459
2460 // check if "source" tile buffer is different from "current" tile buffer
2461 if (pLOEvent->m_pTileBuffer != &*priv->m_pTileBuffer)
2462 {
2463 pLOEvent->m_pTileBuffer = nullptr;
2464 g_task_return_new_error(task,
2467 "TileBuffer has changed");
2468 return;
2469 }
2470 std::unique_ptr<TileBuffer>& buffer = priv->m_pTileBuffer;
2471 if (buffer->hasValidTile(pLOEvent->m_nPaintTileX, pLOEvent->m_nPaintTileY))
2472 return;
2473
2474 cairo_surface_t *pSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, nTileSizePixelsScaled, nTileSizePixelsScaled);
2475 if (cairo_surface_status(pSurface) != CAIRO_STATUS_SUCCESS)
2476 {
2477 cairo_surface_destroy(pSurface);
2478 g_task_return_new_error(task,
2481 "Error allocating Surface");
2482 return;
2483 }
2484
2485 unsigned char* pBuffer = cairo_image_surface_get_data(pSurface);
2486 GdkRectangle aTileRectangle;
2487 aTileRectangle.x = pixelToTwip(nTileSizePixelsScaled, pLOEvent->m_fPaintTileZoom * nScaleFactor) * pLOEvent->m_nPaintTileY;
2488 aTileRectangle.y = pixelToTwip(nTileSizePixelsScaled, pLOEvent->m_fPaintTileZoom * nScaleFactor) * pLOEvent->m_nPaintTileX;
2489
2490 std::unique_lock<std::mutex> aGuard(g_aLOKMutex);
2491 setDocumentView(priv->m_pDocument, priv->m_nViewId);
2492 std::stringstream ss;
2493 GTimer* aTimer = g_timer_new();
2494 gulong nElapsedMs;
2495 ss << "lok::Document::paintTile(" << static_cast<void*>(pBuffer) << ", "
2496 << nTileSizePixelsScaled << ", " << nTileSizePixelsScaled << ", "
2497 << aTileRectangle.x << ", " << aTileRectangle.y << ", "
2498 << pixelToTwip(nTileSizePixelsScaled, pLOEvent->m_fPaintTileZoom * nScaleFactor) << ", "
2499 << pixelToTwip(nTileSizePixelsScaled, pLOEvent->m_fPaintTileZoom * nScaleFactor) << ")";
2500
2501 priv->m_pDocument->pClass->paintTile(priv->m_pDocument,
2502 pBuffer,
2503 nTileSizePixelsScaled, nTileSizePixelsScaled,
2504 aTileRectangle.x, aTileRectangle.y,
2505 pixelToTwip(nTileSizePixelsScaled, pLOEvent->m_fPaintTileZoom * nScaleFactor),
2506 pixelToTwip(nTileSizePixelsScaled, pLOEvent->m_fPaintTileZoom * nScaleFactor));
2507 aGuard.unlock();
2508
2509 g_timer_elapsed(aTimer, &nElapsedMs);
2510 ss << " rendered in " << (nElapsedMs / 1000.) << " milliseconds";
2511 g_info("%s", ss.str().c_str());
2512 g_timer_destroy(aTimer);
2513
2514 cairo_surface_mark_dirty(pSurface);
2515
2516 // Its likely that while the tilebuffer has changed, one of the paint tile
2517 // requests has passed the previous check at start of this function, and has
2518 // rendered the tile already. We want to stop such rendered tiles from being
2519 // stored in new tile buffer.
2520 if (pLOEvent->m_pTileBuffer != &*priv->m_pTileBuffer)
2521 {
2522 pLOEvent->m_pTileBuffer = nullptr;
2523 g_task_return_new_error(task,
2526 "TileBuffer has changed");
2527 return;
2528 }
2529
2530 g_task_return_pointer(task, pSurface, reinterpret_cast<GDestroyNotify>(cairo_surface_destroy));
2531}
2532
2533
2534static void
2535lokThreadFunc(gpointer data, gpointer /*user_data*/)
2536{
2537 GTask* task = G_TASK(data);
2538 LOEvent* pLOEvent = static_cast<LOEvent*>(g_task_get_task_data(task));
2539 LOKDocView* pDocView = LOK_DOC_VIEW(g_task_get_source_object(task));
2540 LOKDocViewPrivate& priv = getPrivate(pDocView);
2541
2542 switch (pLOEvent->m_nType)
2543 {
2544 case LOK_LOAD_DOC:
2546 break;
2547 case LOK_POST_COMMAND:
2548 postCommandInThread(task);
2549 break;
2550 case LOK_SET_EDIT:
2551 setEditInThread(task);
2552 break;
2553 case LOK_SET_PART:
2554 setPartInThread(task);
2555 break;
2556 case LOK_SET_PARTMODE:
2557 setPartmodeInThread(task);
2558 break;
2559 case LOK_POST_KEY:
2560 // view-only/editable mode already checked during signal key signal emission
2562 break;
2563 case LOK_PAINT_TILE:
2564 paintTileInThread(task);
2565 break;
2568 break;
2570 if (priv->m_bEdit)
2572 else
2573 g_info ("LOK_SET_GRAPHIC_SELECTION: skipping graphical operation in view-only mode");
2574 break;
2577 break;
2578 }
2579
2580 g_object_unref(task);
2581}
2582
2583static void
2584onStyleContextChanged (LOKDocView* pDocView)
2585{
2586 // The scale factor might have changed
2587 updateClientZoom (pDocView);
2588 gtk_widget_queue_draw (GTK_WIDGET (pDocView));
2589}
2590
2591static void lok_doc_view_init (LOKDocView* pDocView)
2592{
2593 LOKDocViewPrivate& priv = getPrivate(pDocView);
2594 priv.m_pImpl = new LOKDocViewPrivateImpl();
2595
2596 gtk_widget_add_events(GTK_WIDGET(pDocView),
2597 GDK_BUTTON_PRESS_MASK
2598 |GDK_BUTTON_RELEASE_MASK
2599 |GDK_BUTTON_MOTION_MASK
2600 |GDK_KEY_PRESS_MASK
2601 |GDK_KEY_RELEASE_MASK);
2602
2603 priv->lokThreadPool = g_thread_pool_new(lokThreadFunc,
2604 nullptr,
2605 1,
2606 FALSE,
2607 nullptr);
2608
2609 g_signal_connect (pDocView, "style-updated", G_CALLBACK(onStyleContextChanged), nullptr);
2610}
2611
2612static void lok_doc_view_set_property (GObject* object, guint propId, const GValue *value, GParamSpec *pspec)
2613{
2614 LOKDocView* pDocView = LOK_DOC_VIEW (object);
2615 LOKDocViewPrivate& priv = getPrivate(pDocView);
2616 bool bDocPasswordEnabled = priv->m_nLOKFeatures & LOK_FEATURE_DOCUMENT_PASSWORD;
2617 bool bDocPasswordToModifyEnabled = priv->m_nLOKFeatures & LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY;
2618 bool bTiledAnnotationsEnabled = !(priv->m_nLOKFeatures & LOK_FEATURE_NO_TILED_ANNOTATIONS);
2619
2620 switch (propId)
2621 {
2622 case PROP_LO_PATH:
2623 priv->m_aLOPath = g_value_get_string (value);
2624 break;
2625 case PROP_LO_UNIPOLL:
2626 priv->m_bUnipoll = g_value_get_boolean (value);
2627 break;
2628 case PROP_LO_POINTER:
2629 priv->m_pOffice = static_cast<LibreOfficeKit*>(g_value_get_pointer(value));
2630 break;
2632 if (const gchar* pUserProfile = g_value_get_string(value))
2633 priv->m_aUserProfileURL = pUserProfile;
2634 break;
2635 case PROP_DOC_PATH:
2636 priv->m_aDocPath = g_value_get_string (value);
2637 break;
2638 case PROP_DOC_POINTER:
2639 priv->m_pDocument = static_cast<LibreOfficeKitDocument*>(g_value_get_pointer(value));
2640 priv->m_eDocumentType = static_cast<LibreOfficeKitDocumentType>(priv->m_pDocument->pClass->getDocumentType(priv->m_pDocument));
2641 break;
2642 case PROP_EDITABLE:
2643 lok_doc_view_set_edit (pDocView, g_value_get_boolean (value));
2644 break;
2645 case PROP_ZOOM:
2646 lok_doc_view_set_zoom (pDocView, g_value_get_float (value));
2647 break;
2648 case PROP_DOC_WIDTH:
2649 priv->m_nDocumentWidthTwips = g_value_get_long (value);
2650 break;
2651 case PROP_DOC_HEIGHT:
2652 priv->m_nDocumentHeightTwips = g_value_get_long (value);
2653 break;
2654 case PROP_DOC_PASSWORD:
2655 if (bool(g_value_get_boolean (value)) != bDocPasswordEnabled)
2656 {
2657 priv->m_nLOKFeatures = priv->m_nLOKFeatures ^ LOK_FEATURE_DOCUMENT_PASSWORD;
2658 priv->m_pOffice->pClass->setOptionalFeatures(priv->m_pOffice, priv->m_nLOKFeatures);
2659 }
2660 break;
2662 if ( bool(g_value_get_boolean (value)) != bDocPasswordToModifyEnabled)
2663 {
2664 priv->m_nLOKFeatures = priv->m_nLOKFeatures ^ LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY;
2665 priv->m_pOffice->pClass->setOptionalFeatures(priv->m_pOffice, priv->m_nLOKFeatures);
2666 }
2667 break;
2669 if ( bool(g_value_get_boolean (value)) != bTiledAnnotationsEnabled)
2670 {
2671 priv->m_nLOKFeatures = priv->m_nLOKFeatures ^ LOK_FEATURE_NO_TILED_ANNOTATIONS;
2672 priv->m_pOffice->pClass->setOptionalFeatures(priv->m_pOffice, priv->m_nLOKFeatures);
2673 }
2674 break;
2675 default:
2676 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propId, pspec);
2677 }
2678}
2679
2680static void lok_doc_view_get_property (GObject* object, guint propId, GValue *value, GParamSpec *pspec)
2681{
2682 LOKDocView* pDocView = LOK_DOC_VIEW (object);
2683 LOKDocViewPrivate& priv = getPrivate(pDocView);
2684
2685 switch (propId)
2686 {
2687 case PROP_LO_PATH:
2688 g_value_set_string (value, priv->m_aLOPath.c_str());
2689 break;
2690 case PROP_LO_UNIPOLL:
2691 g_value_set_boolean (value, priv->m_bUnipoll);
2692 break;
2693 case PROP_LO_POINTER:
2694 g_value_set_pointer(value, priv->m_pOffice);
2695 break;
2697 g_value_set_string(value, priv->m_aUserProfileURL.c_str());
2698 break;
2699 case PROP_DOC_PATH:
2700 g_value_set_string (value, priv->m_aDocPath.c_str());
2701 break;
2702 case PROP_DOC_POINTER:
2703 g_value_set_pointer(value, priv->m_pDocument);
2704 break;
2705 case PROP_EDITABLE:
2706 g_value_set_boolean (value, priv->m_bEdit);
2707 break;
2708 case PROP_LOAD_PROGRESS:
2709 g_value_set_double (value, priv->m_nLoadProgress);
2710 break;
2711 case PROP_ZOOM:
2712 g_value_set_float (value, priv->m_fZoom);
2713 break;
2714 case PROP_IS_LOADING:
2715 g_value_set_boolean (value, priv->m_bIsLoading);
2716 break;
2718 g_value_set_boolean (value, priv->m_bInit);
2719 break;
2720 case PROP_DOC_WIDTH:
2721 g_value_set_long (value, priv->m_nDocumentWidthTwips);
2722 break;
2723 case PROP_DOC_HEIGHT:
2724 g_value_set_long (value, priv->m_nDocumentHeightTwips);
2725 break;
2726 case PROP_CAN_ZOOM_IN:
2727 g_value_set_boolean (value, priv->m_bCanZoomIn);
2728 break;
2729 case PROP_CAN_ZOOM_OUT:
2730 g_value_set_boolean (value, priv->m_bCanZoomOut);
2731 break;
2732 case PROP_DOC_PASSWORD:
2733 g_value_set_boolean (value, (priv->m_nLOKFeatures & LOK_FEATURE_DOCUMENT_PASSWORD) != 0);
2734 break;
2736 g_value_set_boolean (value, (priv->m_nLOKFeatures & LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY) != 0);
2737 break;
2739 g_value_set_boolean (value, !(priv->m_nLOKFeatures & LOK_FEATURE_NO_TILED_ANNOTATIONS));
2740 break;
2741 default:
2742 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propId, pspec);
2743 }
2744}
2745
2746static gboolean lok_doc_view_draw (GtkWidget* pWidget, cairo_t* pCairo)
2747{
2748 LOKDocView *pDocView = LOK_DOC_VIEW (pWidget);
2749
2750 renderDocument (pDocView, pCairo);
2751 renderOverlay (pDocView, pCairo);
2752
2753 return FALSE;
2754}
2755
2756//rhbz#1444437 finalize may not occur immediately when this widget is destroyed
2757//it may happen during GC of javascript, e.g. in gnome-documents but "destroy"
2758//will be called promptly, so close documents in destroy, not finalize
2759static void lok_doc_view_destroy (GtkWidget* widget)
2760{
2761 LOKDocView* pDocView = LOK_DOC_VIEW (widget);
2762 LOKDocViewPrivate& priv = getPrivate(pDocView);
2763
2764 // Ignore notifications sent to this view on shutdown.
2765 std::unique_lock<std::mutex> aGuard(g_aLOKMutex);
2766 if (priv->m_pDocument)
2767 {
2768 setDocumentView(priv->m_pDocument, priv->m_nViewId);
2769 priv->m_pDocument->pClass->registerCallback(priv->m_pDocument, nullptr, nullptr);
2770 }
2771
2772 if (priv->lokThreadPool)
2773 {
2774 g_thread_pool_free(priv->lokThreadPool, true, true);
2775 priv->lokThreadPool = nullptr;
2776 }
2777
2778 aGuard.unlock();
2779
2780 if (priv->m_pDocument)
2781 {
2782 // This call may drop several views - e.g., embedded OLE in-place clients
2783 priv->m_pDocument->pClass->destroyView(priv->m_pDocument, priv->m_nViewId);
2784 if (priv->m_pDocument->pClass->getViewsCount(priv->m_pDocument) == 0)
2785 {
2786 // Last view(s) gone
2787 priv->m_pDocument->pClass->destroy (priv->m_pDocument);
2788 priv->m_pDocument = nullptr;
2789 if (priv->m_pOffice)
2790 {
2791 priv->m_pOffice->pClass->destroy (priv->m_pOffice);
2792 priv->m_pOffice = nullptr;
2793 }
2794 }
2795 }
2796
2797 GTK_WIDGET_CLASS (lok_doc_view_parent_class)->destroy (widget);
2798}
2799
2800static void lok_doc_view_finalize (GObject* object)
2801{
2802 LOKDocView* pDocView = LOK_DOC_VIEW (object);
2803 LOKDocViewPrivate& priv = getPrivate(pDocView);
2804
2805 delete priv.m_pImpl;
2806 priv.m_pImpl = nullptr;
2807
2808 G_OBJECT_CLASS (lok_doc_view_parent_class)->finalize (object);
2809}
2810
2811// kicks the mainloop awake
2812static gboolean timeout_wakeup(void *)
2813{
2814 return FALSE;
2815}
2816
2817// integrate our mainloop with LOK's
2818static int lok_poll_callback(void*, int timeoutUs)
2819{
2820 bool bWasEvent(false);
2821 if (timeoutUs > 0)
2822 {
2823 guint timeout = g_timeout_add(timeoutUs / 1000, timeout_wakeup, nullptr);
2824 bWasEvent = g_main_context_iteration(nullptr, true);
2825 g_source_remove(timeout);
2826 }
2827 else
2828 bWasEvent = g_main_context_iteration(nullptr, timeoutUs < 0);
2829
2830 return bWasEvent ? 1 : 0;
2831}
2832
2833// thread-safe wakeup of our mainloop
2834static void lok_wake_callback(void *)
2835{
2836 g_main_context_wakeup(nullptr);
2837}
2838
2839static gboolean spin_lok_loop(void *pData)
2840{
2841 LOKDocView *pDocView = LOK_DOC_VIEW (pData);
2842 LOKDocViewPrivate& priv = getPrivate(pDocView);
2843 priv->m_pOffice->pClass->runLoop(priv->m_pOffice, lok_poll_callback, lok_wake_callback, nullptr);
2844 return FALSE;
2845}
2846
2847// Update the client's view size
2848static void updateClientZoom(LOKDocView *pDocView)
2849{
2850 LOKDocViewPrivate& priv = getPrivate(pDocView);
2851 if (!priv->m_fZoom)
2852 return; // Not initialized yet?
2853 gint nScaleFactor = gtk_widget_get_scale_factor(GTK_WIDGET(pDocView));
2854 gint nTileSizePixelsScaled = nTileSizePixels * nScaleFactor;
2855 GError* error = nullptr;
2856
2857 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
2858 LOEvent* pLOEvent = new LOEvent(LOK_SET_CLIENT_ZOOM);
2859 pLOEvent->m_nTilePixelWidth = nTileSizePixelsScaled;
2860 pLOEvent->m_nTilePixelHeight = nTileSizePixelsScaled;
2861 pLOEvent->m_nTileTwipWidth = pixelToTwip(nTileSizePixelsScaled, priv->m_fZoom * nScaleFactor);
2862 pLOEvent->m_nTileTwipHeight = pixelToTwip(nTileSizePixelsScaled, priv->m_fZoom * nScaleFactor);
2863 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
2864
2865 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
2866 if (error != nullptr)
2867 {
2868 g_warning("Unable to call LOK_SET_CLIENT_ZOOM: %s", error->message);
2869 g_clear_error(&error);
2870 }
2871 g_object_unref(task);
2872
2873 priv->m_nTileSizeTwips = pixelToTwip(nTileSizePixelsScaled, priv->m_fZoom * nScaleFactor);
2874}
2875
2876static gboolean lok_doc_view_initable_init (GInitable *initable, GCancellable* /*cancellable*/, GError **error)
2877{
2878 LOKDocView *pDocView = LOK_DOC_VIEW (initable);
2879 LOKDocViewPrivate& priv = getPrivate(pDocView);
2880
2881 if (priv->m_pOffice != nullptr)
2882 return true;
2883
2884 if (priv->m_bUnipoll)
2885 (void)g_setenv("SAL_LOK_OPTIONS", "unipoll", FALSE);
2886
2887 static const char testingLangs[] = "de_DE en_GB en_US es_ES fr_FR it nl pt_BR pt_PT ru";
2888 (void)g_setenv("LOK_ALLOWLIST_LANGUAGES", testingLangs, FALSE);
2889
2890 priv->m_pOffice = lok_init_2(priv->m_aLOPath.c_str(), priv->m_aUserProfileURL.empty() ? nullptr : priv->m_aUserProfileURL.c_str());
2891
2892 if (priv->m_pOffice == nullptr)
2893 {
2894 g_set_error (error,
2895 g_quark_from_static_string ("LOK initialization error"), 0,
2896 "Failed to get LibreOfficeKit context. Make sure path (%s) is correct",
2897 priv->m_aLOPath.c_str());
2898 return FALSE;
2899 }
2900 priv->m_nLOKFeatures |= LOK_FEATURE_PART_IN_INVALIDATION_CALLBACK;
2901 priv->m_nLOKFeatures |= LOK_FEATURE_VIEWID_IN_VISCURSOR_INVALIDATION_CALLBACK;
2902 priv->m_pOffice->pClass->setOptionalFeatures(priv->m_pOffice, priv->m_nLOKFeatures);
2903
2904 if (priv->m_bUnipoll)
2905 g_idle_add(spin_lok_loop, pDocView);
2906
2907 return true;
2908}
2909
2910static void lok_doc_view_initable_iface_init (GInitableIface *iface)
2911{
2912 iface->init = lok_doc_view_initable_init;
2913}
2914
2915static void lok_doc_view_class_init (LOKDocViewClass* pClass)
2916{
2917 GObjectClass *pGObjectClass = G_OBJECT_CLASS(pClass);
2918 GtkWidgetClass *pWidgetClass = GTK_WIDGET_CLASS(pClass);
2919
2920 pGObjectClass->get_property = lok_doc_view_get_property;
2921 pGObjectClass->set_property = lok_doc_view_set_property;
2922 pGObjectClass->finalize = lok_doc_view_finalize;
2923
2924 pWidgetClass->draw = lok_doc_view_draw;
2925 pWidgetClass->button_press_event = lok_doc_view_signal_button;
2926 pWidgetClass->button_release_event = lok_doc_view_signal_button;
2927 pWidgetClass->key_press_event = signalKey;
2928 pWidgetClass->key_release_event = signalKey;
2929 pWidgetClass->motion_notify_event = lok_doc_view_signal_motion;
2930 pWidgetClass->destroy = lok_doc_view_destroy;
2931
2938 g_param_spec_string("lopath",
2939 "LO Path",
2940 "LibreOffice Install Path",
2941 nullptr,
2942 static_cast<GParamFlags>(G_PARAM_READWRITE |
2943 G_PARAM_CONSTRUCT_ONLY |
2944 G_PARAM_STATIC_STRINGS));
2945
2952 g_param_spec_boolean("unipoll",
2953 "Unified Polling",
2954 "Whether we use a custom unified polling loop",
2955 FALSE,
2956 static_cast<GParamFlags>(G_PARAM_READWRITE |
2957 G_PARAM_CONSTRUCT_ONLY |
2958 G_PARAM_STATIC_STRINGS));
2966 g_param_spec_pointer("lopointer",
2967 "LO Pointer",
2968 "A LibreOfficeKit* from lok_init()",
2969 static_cast<GParamFlags>(G_PARAM_READWRITE |
2970 G_PARAM_CONSTRUCT_ONLY |
2971 G_PARAM_STATIC_STRINGS));
2972
2979 g_param_spec_string("userprofileurl",
2980 "User profile path",
2981 "LibreOffice user profile path",
2982 nullptr,
2983 static_cast<GParamFlags>(G_PARAM_READWRITE |
2984 G_PARAM_CONSTRUCT_ONLY |
2985 G_PARAM_STATIC_STRINGS));
2986
2993 g_param_spec_string("docpath",
2994 "Document Path",
2995 "The URI of the document to open",
2996 nullptr,
2997 static_cast<GParamFlags>(G_PARAM_READWRITE |
2998 G_PARAM_STATIC_STRINGS));
2999
3007 g_param_spec_pointer("docpointer",
3008 "Document Pointer",
3009 "A LibreOfficeKitDocument* from documentLoad()",
3010 static_cast<GParamFlags>(G_PARAM_READWRITE |
3011 G_PARAM_STATIC_STRINGS));
3012
3019 g_param_spec_boolean("editable",
3020 "Editable",
3021 "Whether the content is in edit mode or not",
3022 FALSE,
3023 static_cast<GParamFlags>(G_PARAM_READWRITE |
3024 G_PARAM_STATIC_STRINGS));
3025
3035 g_param_spec_double("load-progress",
3036 "Estimated Load Progress",
3037 "Shows the progress of the document load operation",
3038 0.0, 1.0, 0.0,
3039 static_cast<GParamFlags>(G_PARAM_READABLE |
3040 G_PARAM_STATIC_STRINGS));
3041
3049 g_param_spec_float("zoom-level",
3050 "Zoom Level",
3051 "The current zoom level of the content",
3052 0, 5.0, 1.0,
3053 static_cast<GParamFlags>(G_PARAM_READWRITE |
3054 G_PARAM_STATIC_STRINGS));
3055
3063 g_param_spec_boolean("is-loading",
3064 "Is Loading",
3065 "Whether the view is loading a document",
3066 FALSE,
3067 static_cast<GParamFlags>(G_PARAM_READABLE |
3068 G_PARAM_STATIC_STRINGS));
3069
3076 g_param_spec_boolean("is-initialized",
3077 "Has initialized",
3078 "Whether the view has completely initialized",
3079 FALSE,
3080 static_cast<GParamFlags>(G_PARAM_READABLE |
3081 G_PARAM_STATIC_STRINGS));
3082
3089 g_param_spec_long("doc-width",
3090 "Document Width",
3091 "Width of the document in twips",
3092 0, G_MAXLONG, 0,
3093 static_cast<GParamFlags>(G_PARAM_READWRITE |
3094 G_PARAM_STATIC_STRINGS));
3095
3102 g_param_spec_long("doc-height",
3103 "Document Height",
3104 "Height of the document in twips",
3105 0, G_MAXLONG, 0,
3106 static_cast<GParamFlags>(G_PARAM_READWRITE |
3107 G_PARAM_STATIC_STRINGS));
3108
3115 g_param_spec_boolean("can-zoom-in",
3116 "Can Zoom In",
3117 "Whether the view can be zoomed in further",
3118 true,
3119 static_cast<GParamFlags>(G_PARAM_READABLE
3120 | G_PARAM_STATIC_STRINGS));
3121
3128 g_param_spec_boolean("can-zoom-out",
3129 "Can Zoom Out",
3130 "Whether the view can be zoomed out further",
3131 true,
3132 static_cast<GParamFlags>(G_PARAM_READABLE
3133 | G_PARAM_STATIC_STRINGS));
3134
3142 g_param_spec_boolean("doc-password",
3143 "Document password capability",
3144 "Whether client supports providing document passwords",
3145 FALSE,
3146 static_cast<GParamFlags>(G_PARAM_READWRITE
3147 | G_PARAM_STATIC_STRINGS));
3148
3155 g_param_spec_boolean("doc-password-to-modify",
3156 "Edit document password capability",
3157 "Whether the client supports providing passwords to edit documents",
3158 FALSE,
3159 static_cast<GParamFlags>(G_PARAM_READWRITE
3160 | G_PARAM_STATIC_STRINGS));
3161
3169 g_param_spec_boolean("tiled-annotations",
3170 "Render comments in tiles",
3171 "Whether the client wants in tile comment rendering",
3172 true,
3173 static_cast<GParamFlags>(G_PARAM_READWRITE
3174 | G_PARAM_STATIC_STRINGS));
3175
3176 g_object_class_install_properties(pGObjectClass, PROP_LAST, properties);
3177
3184 g_signal_new("load-changed",
3185 G_TYPE_FROM_CLASS (pGObjectClass),
3186 G_SIGNAL_RUN_FIRST,
3187 0,
3188 nullptr, nullptr,
3189 g_cclosure_marshal_VOID__DOUBLE,
3190 G_TYPE_NONE, 1,
3191 G_TYPE_DOUBLE);
3192
3199 g_signal_new("edit-changed",
3200 G_TYPE_FROM_CLASS (pGObjectClass),
3201 G_SIGNAL_RUN_FIRST,
3202 0,
3203 nullptr, nullptr,
3204 g_cclosure_marshal_VOID__BOOLEAN,
3205 G_TYPE_NONE, 1,
3206 G_TYPE_BOOLEAN);
3207
3214 g_signal_new("command-changed",
3215 G_TYPE_FROM_CLASS(pGObjectClass),
3216 G_SIGNAL_RUN_FIRST,
3217 0,
3218 nullptr, nullptr,
3219 g_cclosure_marshal_VOID__STRING,
3220 G_TYPE_NONE, 1,
3221 G_TYPE_STRING);
3222
3229 g_signal_new("search-not-found",
3230 G_TYPE_FROM_CLASS(pGObjectClass),
3231 G_SIGNAL_RUN_FIRST,
3232 0,
3233 nullptr, nullptr,
3234 g_cclosure_marshal_VOID__STRING,
3235 G_TYPE_NONE, 1,
3236 G_TYPE_STRING);
3237
3244 g_signal_new("part-changed",
3245 G_TYPE_FROM_CLASS(pGObjectClass),
3246 G_SIGNAL_RUN_FIRST,
3247 0,
3248 nullptr, nullptr,
3249 g_cclosure_marshal_VOID__INT,
3250 G_TYPE_NONE, 1,
3251 G_TYPE_INT);
3252
3259 g_signal_new("size-changed",
3260 G_TYPE_FROM_CLASS(pGObjectClass),
3261 G_SIGNAL_RUN_FIRST,
3262 0,
3263 nullptr, nullptr,
3264 g_cclosure_marshal_VOID__VOID,
3265 G_TYPE_NONE, 1,
3266 G_TYPE_INT);
3267
3274 g_signal_new("hyperlink-clicked",
3275 G_TYPE_FROM_CLASS(pGObjectClass),
3276 G_SIGNAL_RUN_FIRST,
3277 0,
3278 nullptr, nullptr,
3279 g_cclosure_marshal_VOID__STRING,
3280 G_TYPE_NONE, 1,
3281 G_TYPE_STRING);
3282
3292 g_signal_new("cursor-changed",
3293 G_TYPE_FROM_CLASS(pGObjectClass),
3294 G_SIGNAL_RUN_FIRST,
3295 0,
3296 nullptr, nullptr,
3297 g_cclosure_marshal_generic,
3298 G_TYPE_NONE, 4,
3299 G_TYPE_INT, G_TYPE_INT,
3300 G_TYPE_INT, G_TYPE_INT);
3301
3308 g_signal_new("search-result-count",
3309 G_TYPE_FROM_CLASS(pGObjectClass),
3310 G_SIGNAL_RUN_FIRST,
3311 0,
3312 nullptr, nullptr,
3313 g_cclosure_marshal_VOID__STRING,
3314 G_TYPE_NONE, 1,
3315 G_TYPE_STRING);
3316
3324 g_signal_new("command-result",
3325 G_TYPE_FROM_CLASS(pGObjectClass),
3326 G_SIGNAL_RUN_FIRST,
3327 0,
3328 nullptr, nullptr,
3329 g_cclosure_marshal_VOID__STRING,
3330 G_TYPE_NONE, 1,
3331 G_TYPE_STRING);
3332
3339 g_signal_new("address-changed",
3340 G_TYPE_FROM_CLASS(pGObjectClass),
3341 G_SIGNAL_RUN_FIRST,
3342 0,
3343 nullptr, nullptr,
3344 g_cclosure_marshal_VOID__STRING,
3345 G_TYPE_NONE, 1,
3346 G_TYPE_STRING);
3347
3354 g_signal_new("formula-changed",
3355 G_TYPE_FROM_CLASS(pGObjectClass),
3356 G_SIGNAL_RUN_FIRST,
3357 0,
3358 nullptr, nullptr,
3359 g_cclosure_marshal_VOID__STRING,
3360 G_TYPE_NONE, 1,
3361 G_TYPE_STRING);
3362
3369 g_signal_new("text-selection",
3370 G_TYPE_FROM_CLASS(pGObjectClass),
3371 G_SIGNAL_RUN_FIRST,
3372 0,
3373 nullptr, nullptr,
3374 g_cclosure_marshal_VOID__BOOLEAN,
3375 G_TYPE_NONE, 1,
3376 G_TYPE_BOOLEAN);
3377
3384 g_signal_new("content-control",
3385 G_TYPE_FROM_CLASS(pGObjectClass),
3386 G_SIGNAL_RUN_FIRST,
3387 0,
3388 nullptr, nullptr,
3389 g_cclosure_marshal_generic,
3390 G_TYPE_NONE, 1,
3391 G_TYPE_STRING);
3392
3414 g_signal_new("password-required",
3415 G_TYPE_FROM_CLASS(pGObjectClass),
3416 G_SIGNAL_RUN_FIRST,
3417 0,
3418 nullptr, nullptr,
3419 g_cclosure_marshal_generic,
3420 G_TYPE_NONE, 2,
3421 G_TYPE_STRING,
3422 G_TYPE_BOOLEAN);
3423
3449 g_signal_new("comment",
3450 G_TYPE_FROM_CLASS(pGObjectClass),
3451 G_SIGNAL_RUN_FIRST,
3452 0,
3453 nullptr, nullptr,
3454 g_cclosure_marshal_generic,
3455 G_TYPE_NONE, 1,
3456 G_TYPE_STRING);
3457
3475 g_signal_new("ruler",
3476 G_TYPE_FROM_CLASS(pGObjectClass),
3477 G_SIGNAL_RUN_FIRST,
3478 0,
3479 nullptr, nullptr,
3480 g_cclosure_marshal_generic,
3481 G_TYPE_NONE, 1,
3482 G_TYPE_STRING);
3483
3516 g_signal_new("window",
3517 G_TYPE_FROM_CLASS(pGObjectClass),
3518 G_SIGNAL_RUN_FIRST,
3519 0,
3520 nullptr, nullptr,
3521 g_cclosure_marshal_generic,
3522 G_TYPE_NONE, 1,
3523 G_TYPE_STRING);
3524
3536 g_signal_new("invalidate-header",
3537 G_TYPE_FROM_CLASS(pGObjectClass),
3538 G_SIGNAL_RUN_FIRST,
3539 0,
3540 nullptr, nullptr,
3541 g_cclosure_marshal_generic,
3542 G_TYPE_NONE, 1,
3543 G_TYPE_STRING);
3544}
3545
3546SAL_DLLPUBLIC_EXPORT GtkWidget*
3547lok_doc_view_new (const gchar* pPath, GCancellable *cancellable, GError **error)
3548{
3549 return GTK_WIDGET (g_initable_new (LOK_TYPE_DOC_VIEW, cancellable, error,
3550 "lopath", pPath == nullptr ? LOK_PATH : pPath,
3551 "halign", GTK_ALIGN_CENTER,
3552 "valign", GTK_ALIGN_CENTER,
3553 nullptr));
3554}
3555
3556SAL_DLLPUBLIC_EXPORT GtkWidget*
3557lok_doc_view_new_from_user_profile (const gchar* pPath, const gchar* pUserProfile, GCancellable *cancellable, GError **error)
3558{
3559 return GTK_WIDGET(g_initable_new(LOK_TYPE_DOC_VIEW, cancellable, error,
3560 "lopath", pPath == nullptr ? LOK_PATH : pPath,
3561 "userprofileurl", pUserProfile,
3562 "halign", GTK_ALIGN_CENTER,
3563 "valign", GTK_ALIGN_CENTER,
3564 nullptr));
3565}
3566
3567SAL_DLLPUBLIC_EXPORT GtkWidget* lok_doc_view_new_from_widget(LOKDocView* pOldLOKDocView,
3568 const gchar* pRenderingArguments)
3569{
3570 LOKDocViewPrivate& pOldPriv = getPrivate(pOldLOKDocView);
3571 GtkWidget* pNewDocView = GTK_WIDGET(g_initable_new(LOK_TYPE_DOC_VIEW, /*cancellable=*/nullptr, /*error=*/nullptr,
3572 "lopath", pOldPriv->m_aLOPath.c_str(),
3573 "userprofileurl", pOldPriv->m_aUserProfileURL.c_str(),
3574 "lopointer", pOldPriv->m_pOffice,
3575 "docpointer", pOldPriv->m_pDocument,
3576 "halign", GTK_ALIGN_CENTER,
3577 "valign", GTK_ALIGN_CENTER,
3578 nullptr));
3579
3580 // No documentLoad(), just a createView().
3581 LibreOfficeKitDocument* pDocument = lok_doc_view_get_document(LOK_DOC_VIEW(pNewDocView));
3582 LOKDocViewPrivate& pNewPriv = getPrivate(LOK_DOC_VIEW(pNewDocView));
3583 // Store the view id only later in postDocumentLoad(), as
3584 // initializeForRendering() changes the id in Impress.
3585 pDocument->pClass->createView(pDocument);
3586 pNewPriv->m_aRenderingArguments = pRenderingArguments;
3587
3588 postDocumentLoad(pNewDocView);
3589 return pNewDocView;
3590}
3591
3592SAL_DLLPUBLIC_EXPORT gboolean
3593lok_doc_view_open_document_finish (LOKDocView* pDocView, GAsyncResult* res, GError** error)
3594{
3595 GTask* task = G_TASK(res);
3596
3597 g_return_val_if_fail(g_task_is_valid(res, pDocView), false);
3598 g_return_val_if_fail(g_task_get_source_tag(task) == lok_doc_view_open_document, false);
3599 g_return_val_if_fail(error == nullptr || *error == nullptr, false);
3600
3601 return g_task_propagate_boolean(task, error);
3602}
3603
3604SAL_DLLPUBLIC_EXPORT void
3605lok_doc_view_open_document (LOKDocView* pDocView,
3606 const gchar* pPath,
3607 const gchar* pRenderingArguments,
3608 GCancellable* cancellable,
3609 GAsyncReadyCallback callback,
3610 gpointer userdata)
3611{
3612 GTask* task = g_task_new(pDocView, cancellable, callback, userdata);
3613 LOKDocViewPrivate& priv = getPrivate(pDocView);
3614 GError* error = nullptr;
3615
3616 LOEvent* pLOEvent = new LOEvent(LOK_LOAD_DOC);
3617
3618 g_object_set(G_OBJECT(pDocView), "docpath", pPath, nullptr);
3619 if (pRenderingArguments)
3620 priv->m_aRenderingArguments = pRenderingArguments;
3621 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
3622 g_task_set_source_tag(task, reinterpret_cast<gpointer>(lok_doc_view_open_document));
3623
3624 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
3625 if (error != nullptr)
3626 {
3627 g_warning("Unable to call LOK_LOAD_DOC: %s", error->message);
3628 g_clear_error(&error);
3629 }
3630 g_object_unref(task);
3631}
3632
3633SAL_DLLPUBLIC_EXPORT LibreOfficeKitDocument*
3634lok_doc_view_get_document (LOKDocView* pDocView)
3635{
3636 LOKDocViewPrivate& priv = getPrivate(pDocView);
3637 return priv->m_pDocument;
3638}
3639
3640SAL_DLLPUBLIC_EXPORT void
3641lok_doc_view_set_visible_area (LOKDocView* pDocView, GdkRectangle* pVisibleArea)
3642{
3643 if (!pVisibleArea)
3644 return;
3645
3646 LOKDocViewPrivate& priv = getPrivate(pDocView);
3647 priv->m_aVisibleArea = *pVisibleArea;
3648 priv->m_bVisibleAreaSet = true;
3649}
3650
3651namespace {
3652// This used to be rtl::math::approxEqual() but since that isn't inline anymore
3653// in rtl/math.hxx and was moved into libuno_sal as rtl_math_approxEqual() to
3654// cater for representable integer cases and we don't want to link against
3655// libuno_sal, we'll have to have an own implementation. The special large
3656// integer cases seems not be needed here.
3657bool lok_approxEqual(double a, double b)
3658{
3659 static const double e48 = 1.0 / (16777216.0 * 16777216.0);
3660 if (a == b)
3661 return true;
3662 if (a == 0.0 || b == 0.0)
3663 return false;
3664 const double d = fabs(a - b);
3665 return (d < fabs(a) * e48 && d < fabs(b) * e48);
3666}
3667}
3668
3669SAL_DLLPUBLIC_EXPORT void
3670lok_doc_view_set_zoom (LOKDocView* pDocView, float fZoom)
3671{
3672 LOKDocViewPrivate& priv = getPrivate(pDocView);
3673
3674 if (!priv->m_pDocument)
3675 return;
3676
3677 // Clamp the input value in [MIN_ZOOM, MAX_ZOOM]
3678 fZoom = fZoom < MIN_ZOOM ? MIN_ZOOM : fZoom;
3679 fZoom = std::min(fZoom, MAX_ZOOM);
3680
3681 if (lok_approxEqual(fZoom, priv->m_fZoom))
3682 return;
3683
3684 gint nScaleFactor = gtk_widget_get_scale_factor(GTK_WIDGET(pDocView));
3685 gint nTileSizePixelsScaled = nTileSizePixels * nScaleFactor;
3686 priv->m_fZoom = fZoom;
3687 long nDocumentWidthPixels = twipToPixel(priv->m_nDocumentWidthTwips, fZoom * nScaleFactor);
3688 long nDocumentHeightPixels = twipToPixel(priv->m_nDocumentHeightTwips, fZoom * nScaleFactor);
3689 // Total number of columns in this document.
3690 guint nColumns = ceil(static_cast<double>(nDocumentWidthPixels) / nTileSizePixelsScaled);
3691 priv->m_pTileBuffer = std::make_unique<TileBuffer>(nColumns, nScaleFactor);
3692 gtk_widget_set_size_request(GTK_WIDGET(pDocView),
3693 nDocumentWidthPixels / nScaleFactor,
3694 nDocumentHeightPixels / nScaleFactor);
3695
3696 g_object_notify_by_pspec(G_OBJECT(pDocView), properties[PROP_ZOOM]);
3697
3698 // set properties to indicate if view can be further zoomed in/out
3699 bool bCanZoomIn = priv->m_fZoom < MAX_ZOOM;
3700 bool bCanZoomOut = priv->m_fZoom > MIN_ZOOM;
3701 if (bCanZoomIn != bool(priv->m_bCanZoomIn))
3702 {
3703 priv->m_bCanZoomIn = bCanZoomIn;
3704 g_object_notify_by_pspec(G_OBJECT(pDocView), properties[PROP_CAN_ZOOM_IN]);
3705 }
3706 if (bCanZoomOut != bool(priv->m_bCanZoomOut))
3707 {
3708 priv->m_bCanZoomOut = bCanZoomOut;
3709 g_object_notify_by_pspec(G_OBJECT(pDocView), properties[PROP_CAN_ZOOM_OUT]);
3710 }
3711
3712 updateClientZoom(pDocView);
3713}
3714
3715SAL_DLLPUBLIC_EXPORT gfloat
3716lok_doc_view_get_zoom (LOKDocView* pDocView)
3717{
3718 LOKDocViewPrivate& priv = getPrivate(pDocView);
3719 return priv->m_fZoom;
3720}
3721
3722SAL_DLLPUBLIC_EXPORT gint
3723lok_doc_view_get_parts (LOKDocView* pDocView)
3724{
3725 LOKDocViewPrivate& priv = getPrivate(pDocView);
3726 if (!priv->m_pDocument)
3727 return -1;
3728
3729 std::scoped_lock<std::mutex> aGuard(g_aLOKMutex);
3730 setDocumentView(priv->m_pDocument, priv->m_nViewId);
3731 return priv->m_pDocument->pClass->getParts( priv->m_pDocument );
3732}
3733
3734SAL_DLLPUBLIC_EXPORT gint
3735lok_doc_view_get_part (LOKDocView* pDocView)
3736{
3737 LOKDocViewPrivate& priv = getPrivate(pDocView);
3738 if (!priv->m_pDocument)
3739 return -1;
3740
3741 std::scoped_lock<std::mutex> aGuard(g_aLOKMutex);
3742 setDocumentView(priv->m_pDocument, priv->m_nViewId);
3743 return priv->m_pDocument->pClass->getPart( priv->m_pDocument );
3744}
3745
3746SAL_DLLPUBLIC_EXPORT void
3747lok_doc_view_set_part (LOKDocView* pDocView, int nPart)
3748{
3749 LOKDocViewPrivate& priv = getPrivate(pDocView);
3750 if (!priv->m_pDocument)
3751 return;
3752
3753 if (nPart < 0 || nPart >= priv->m_nParts)
3754 {
3755 g_warning("Invalid part request : %d", nPart);
3756 return;
3757 }
3758
3759 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
3760 LOEvent* pLOEvent = new LOEvent(LOK_SET_PART);
3761 GError* error = nullptr;
3762
3763 pLOEvent->m_nPart = nPart;
3764 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
3765
3766 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
3767 if (error != nullptr)
3768 {
3769 g_warning("Unable to call LOK_SET_PART: %s", error->message);
3770 g_clear_error(&error);
3771 }
3772 g_object_unref(task);
3773 priv->m_nPartId = nPart;
3774}
3775
3776SAL_DLLPUBLIC_EXPORT void lok_doc_view_send_content_control_event(LOKDocView* pDocView,
3777 const gchar* pArguments)
3778{
3779 LOKDocViewPrivate& priv = getPrivate(pDocView);
3780 if (!priv->m_pDocument)
3781 {
3782 return;
3783 }
3784
3785 std::scoped_lock<std::mutex> aGuard(g_aLOKMutex);
3786 setDocumentView(priv->m_pDocument, priv->m_nViewId);
3787 return priv->m_pDocument->pClass->sendContentControlEvent(priv->m_pDocument, pArguments);
3788}
3789
3790SAL_DLLPUBLIC_EXPORT gchar*
3791lok_doc_view_get_part_name (LOKDocView* pDocView, int nPart)
3792{
3793 LOKDocViewPrivate& priv = getPrivate(pDocView);
3794 if (!priv->m_pDocument)
3795 return nullptr;
3796
3797 std::scoped_lock<std::mutex> aGuard(g_aLOKMutex);
3798 setDocumentView(priv->m_pDocument, priv->m_nViewId);
3799 return priv->m_pDocument->pClass->getPartName( priv->m_pDocument, nPart );
3800}
3801
3802SAL_DLLPUBLIC_EXPORT void
3803lok_doc_view_set_partmode(LOKDocView* pDocView,
3804 int nPartMode)
3805{
3806 LOKDocViewPrivate& priv = getPrivate(pDocView);
3807 if (!priv->m_pDocument)
3808 return;
3809
3810 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
3811 LOEvent* pLOEvent = new LOEvent(LOK_SET_PARTMODE);
3812 GError* error = nullptr;
3813
3814 pLOEvent->m_nPartMode = nPartMode;
3815 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
3816
3817 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
3818 if (error != nullptr)
3819 {
3820 g_warning("Unable to call LOK_SET_PARTMODE: %s", error->message);
3821 g_clear_error(&error);
3822 }
3823 g_object_unref(task);
3824}
3825
3826SAL_DLLPUBLIC_EXPORT void
3827lok_doc_view_reset_view(LOKDocView* pDocView)
3828{
3829 LOKDocViewPrivate& priv = getPrivate(pDocView);
3830
3831 if (priv->m_pTileBuffer != nullptr)
3832 priv->m_pTileBuffer->resetAllTiles();
3833 priv->m_nLoadProgress = 0.0;
3834
3835 memset(&priv->m_aVisibleCursor, 0, sizeof(priv->m_aVisibleCursor));
3836 priv->m_bCursorOverlayVisible = false;
3837 priv->m_bCursorVisible = false;
3838
3839 priv->m_nLastButtonPressTime = 0;
3840 priv->m_nLastButtonReleaseTime = 0;
3841 priv->m_aTextSelectionRectangles.clear();
3842 priv->m_aContentControlRectangles.clear();
3843
3844 memset(&priv->m_aTextSelectionStart, 0, sizeof(priv->m_aTextSelectionStart));
3845 memset(&priv->m_aTextSelectionEnd, 0, sizeof(priv->m_aTextSelectionEnd));
3846 memset(&priv->m_aGraphicSelection, 0, sizeof(priv->m_aGraphicSelection));
3847 priv->m_bInDragGraphicSelection = false;
3848 memset(&priv->m_aCellCursor, 0, sizeof(priv->m_aCellCursor));
3849
3850 cairo_surface_destroy(priv->m_pHandleStart);
3851 priv->m_pHandleStart = nullptr;
3852 memset(&priv->m_aHandleStartRect, 0, sizeof(priv->m_aHandleStartRect));
3853 priv->m_bInDragStartHandle = false;
3854
3855 cairo_surface_destroy(priv->m_pHandleMiddle);
3856 priv->m_pHandleMiddle = nullptr;
3857 memset(&priv->m_aHandleMiddleRect, 0, sizeof(priv->m_aHandleMiddleRect));
3858 priv->m_bInDragMiddleHandle = false;
3859
3860 cairo_surface_destroy(priv->m_pHandleEnd);
3861 priv->m_pHandleEnd = nullptr;
3862 memset(&priv->m_aHandleEndRect, 0, sizeof(priv->m_aHandleEndRect));
3863 priv->m_bInDragEndHandle = false;
3864
3865 memset(&priv->m_aGraphicHandleRects, 0, sizeof(priv->m_aGraphicHandleRects));
3866 memset(&priv->m_bInDragGraphicHandles, 0, sizeof(priv->m_bInDragGraphicHandles));
3867
3868 gtk_widget_queue_draw(GTK_WIDGET(pDocView));
3869}
3870
3871SAL_DLLPUBLIC_EXPORT void
3872lok_doc_view_set_edit(LOKDocView* pDocView,
3873 gboolean bEdit)
3874{
3875 LOKDocViewPrivate& priv = getPrivate(pDocView);
3876 if (!priv->m_pDocument)
3877 return;
3878
3879 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
3880 LOEvent* pLOEvent = new LOEvent(LOK_SET_EDIT);
3881 GError* error = nullptr;
3882
3883 pLOEvent->m_bEdit = bEdit;
3884 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
3885
3886 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
3887 if (error != nullptr)
3888 {
3889 g_warning("Unable to call LOK_SET_EDIT: %s", error->message);
3890 g_clear_error(&error);
3891 }
3892 g_object_unref(task);
3893}
3894
3895SAL_DLLPUBLIC_EXPORT gboolean
3896lok_doc_view_get_edit (LOKDocView* pDocView)
3897{
3898 LOKDocViewPrivate& priv = getPrivate(pDocView);
3899 return priv->m_bEdit;
3900}
3901
3902SAL_DLLPUBLIC_EXPORT void
3903lok_doc_view_post_command (LOKDocView* pDocView,
3904 const gchar* pCommand,
3905 const gchar* pArguments,
3906 gboolean bNotifyWhenFinished)
3907{
3908 LOKDocViewPrivate& priv = getPrivate(pDocView);
3909 if (!priv->m_pDocument)
3910 return;
3911
3912 if (priv->m_bEdit)
3913 LOKPostCommand(pDocView, pCommand, pArguments, bNotifyWhenFinished);
3914 else
3915 g_info ("LOK_POST_COMMAND: ignoring commands in view-only mode");
3916}
3917
3918SAL_DLLPUBLIC_EXPORT gchar *
3920 const gchar* pCommand)
3921{
3922 g_return_val_if_fail (LOK_IS_DOC_VIEW (pDocView), nullptr);
3923 g_return_val_if_fail (pCommand != nullptr, nullptr);
3924
3925 LibreOfficeKitDocument* pDocument = lok_doc_view_get_document(pDocView);
3926 if (!pDocument)
3927 return nullptr;
3928
3929 return pDocument->pClass->getCommandValues(pDocument, pCommand);
3930}
3931
3932SAL_DLLPUBLIC_EXPORT void
3933lok_doc_view_find_prev (LOKDocView* pDocView,
3934 const gchar* pText,
3935 gboolean bHighlightAll)
3936{
3937 doSearch(pDocView, pText, true, bHighlightAll);
3938}
3939
3940SAL_DLLPUBLIC_EXPORT void
3941lok_doc_view_find_next (LOKDocView* pDocView,
3942 const gchar* pText,
3943 gboolean bHighlightAll)
3944{
3945 doSearch(pDocView, pText, false, bHighlightAll);
3946}
3947
3948SAL_DLLPUBLIC_EXPORT void
3949lok_doc_view_highlight_all (LOKDocView* pDocView,
3950 const gchar* pText)
3951{
3952 doSearch(pDocView, pText, false, true);
3953}
3954
3955SAL_DLLPUBLIC_EXPORT gchar*
3956lok_doc_view_copy_selection (LOKDocView* pDocView,
3957 const gchar* pMimeType,
3958 gchar** pUsedMimeType)
3959{
3960 LibreOfficeKitDocument* pDocument = lok_doc_view_get_document(pDocView);
3961 if (!pDocument)
3962 return nullptr;
3963
3964 std::stringstream ss;
3965 ss << "lok::Document::getTextSelection('" << pMimeType << "')";
3966 g_info("%s", ss.str().c_str());
3967 return pDocument->pClass->getTextSelection(pDocument, pMimeType, pUsedMimeType);
3968}
3969
3970SAL_DLLPUBLIC_EXPORT gboolean
3971lok_doc_view_paste (LOKDocView* pDocView,
3972 const gchar* pMimeType,
3973 const gchar* pData,
3974 gsize nSize)
3975{
3976 LOKDocViewPrivate& priv = getPrivate(pDocView);
3977 LibreOfficeKitDocument* pDocument = priv->m_pDocument;
3978 bool ret = false;
3979
3980 if (!pDocument)
3981 return false;
3982
3983 if (!priv->m_bEdit)
3984 {
3985 g_info ("ignoring paste in view-only mode");
3986 return ret;
3987 }
3988
3989 if (pData)
3990 {
3991 std::stringstream ss;
3992 ss << "lok::Document::paste('" << pMimeType << "', '" << std::string(pData, nSize) << ", "<<nSize<<"')";
3993 g_info("%s", ss.str().c_str());
3994 ret = pDocument->pClass->paste(pDocument, pMimeType, pData, nSize);
3995 }
3996
3997 return ret;
3998}
3999
4000SAL_DLLPUBLIC_EXPORT void
4002 const gchar* pURL,
4003 const gchar* pPassword)
4004{
4005 LOKDocViewPrivate& priv = getPrivate(pDocView);
4006
4007 priv->m_pOffice->pClass->setDocumentPassword(priv->m_pOffice, pURL, pPassword);
4008}
4009
4010SAL_DLLPUBLIC_EXPORT gchar*
4011lok_doc_view_get_version_info (LOKDocView* pDocView)
4012{
4013 LOKDocViewPrivate& priv = getPrivate(pDocView);
4014
4015 return priv->m_pOffice->pClass->getVersionInfo(priv->m_pOffice);
4016}
4017
4018
4019SAL_DLLPUBLIC_EXPORT gfloat
4020lok_doc_view_pixel_to_twip (LOKDocView* pDocView, float fInput)
4021{
4022 LOKDocViewPrivate& priv = getPrivate(pDocView);
4023 return pixelToTwip(fInput, priv->m_fZoom);
4024}
4025
4026SAL_DLLPUBLIC_EXPORT gfloat
4027lok_doc_view_twip_to_pixel (LOKDocView* pDocView, float fInput)
4028{
4029 LOKDocViewPrivate& priv = getPrivate(pDocView);
4030 return twipToPixel(fInput, priv->m_fZoom);
4031}
4032
4033/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const PropertyValue * pValues
void GCancellable
double d
struct _cairo_surface cairo_surface_t
struct _cairo cairo_t
This class represents a single tile in the tile buffer.
Definition: tilebuffer.hxx:54
cairo_surface_t * getBuffer()
Function to get the pointer to enclosing cairo_surface_t.
Definition: tilebuffer.cxx:34
Any value
int nCount
float y
float x
#define MOUSE_LEFT
#define MOUSE_MIDDLE
#define MOUSE_RIGHT
struct _GtkWidget GtkWidget
#define FALSE
constexpr sal_uInt16 KEY_MOD2
constexpr sal_uInt16 KEY_MOD1
constexpr sal_uInt16 KEY_SHIFT
static std::string getAuthorRenderingArgument(LOKDocViewPrivate &priv)
Looks up the author string from initializeForRendering()'s rendering arguments.
Definition: lokdocview.cxx:853
static GdkRectangle payloadToRectangle(LOKDocView *pDocView, const char *pPayload)
Definition: lokdocview.cxx:993
@ LAST_SIGNAL
Definition: lokdocview.cxx:305
@ CONTENT_CONTROL
Definition: lokdocview.cxx:298
@ SEARCH_NOT_FOUND
Definition: lokdocview.cxx:288
@ PART_CHANGED
Definition: lokdocview.cxx:289
@ INVALIDATE_HEADER
Definition: lokdocview.cxx:303
@ TEXT_SELECTION
Definition: lokdocview.cxx:297
@ FORMULA_CHANGED
Definition: lokdocview.cxx:296
@ WINDOW
Definition: lokdocview.cxx:302
@ COMMAND_CHANGED
Definition: lokdocview.cxx:287
@ RULER
Definition: lokdocview.cxx:301
@ COMMAND_RESULT
Definition: lokdocview.cxx:294
@ SEARCH_RESULT_COUNT
Definition: lokdocview.cxx:293
@ COMMENT
Definition: lokdocview.cxx:300
@ ADDRESS_CHANGED
Definition: lokdocview.cxx:295
@ HYPERLINK_CLICKED
Definition: lokdocview.cxx:291
@ SIZE_CHANGED
Definition: lokdocview.cxx:290
@ CURSOR_CHANGED
Definition: lokdocview.cxx:292
@ EDIT_CHANGED
Definition: lokdocview.cxx:286
@ PASSWORD_REQUIRED
Definition: lokdocview.cxx:299
@ LOAD_CHANGED
Definition: lokdocview.cxx:285
static void setGraphicSelectionInThread(gpointer data)
G_DEFINE_TYPE_WITH_CODE(LOKDocView, lok_doc_view, GTK_TYPE_DRAWING_AREA, G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, lok_doc_view_initable_iface_init))
SAL_DLLPUBLIC_EXPORT void lok_doc_view_open_document(LOKDocView *pDocView, const gchar *pPath, const gchar *pRenderingArguments, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer userdata)
static void refreshSize(LOKDocView *pDocView)
Definition: lokdocview.cxx:874
static gboolean postDocumentLoad(gpointer pData)
Set up LOKDocView after the document is loaded, invoked on the main thread by openDocumentInThread() ...
Definition: lokdocview.cxx:896
static int lok_poll_callback(void *, int timeoutUs)
static void renderGraphicHandle(LOKDocView *pDocView, cairo_t *pCairo, const GdkRectangle &rSelection, const GdkRGBA &rColor)
Renders handles around an rSelection rectangle on pCairo.
static void openDocumentInThread(gpointer data)
static bool handleTextSelectionOnButtonPress(GdkRectangle &aClick, LOKDocView *pDocView)
if handled, returns TRUE else FALSE
Definition: lokdocview.cxx:453
SAL_DLLPUBLIC_EXPORT void lok_doc_view_set_partmode(LOKDocView *pDocView, int nPartMode)
static gboolean signalKey(GtkWidget *pWidget, GdkEventKey *pEvent)
Definition: lokdocview.cxx:646
static void postMouseEventInThread(gpointer data)
static bool handleGraphicSelectionOnButtonPress(GdkRectangle &aClick, LOKDocView *pDocView)
if handled, returns TRUE else FALSE
Definition: lokdocview.cxx:480
SAL_DLLPUBLIC_EXPORT void lok_doc_view_post_command(LOKDocView *pDocView, const gchar *pCommand, const gchar *pArguments, gboolean bNotifyWhenFinished)
#define g_info(...)
Definition: lokdocview.cxx:35
static void setTilesInvalid(LOKDocView *pDocView, const GdkRectangle &rRectangle)
static gboolean lok_doc_view_draw(GtkWidget *pWidget, cairo_t *pCairo)
static void lokThreadFunc(gpointer data, gpointer)
static bool handleTextSelectionOnButtonRelease(LOKDocView *pDocView)
if handled, returns TRUE else FALSE
Definition: lokdocview.cxx:515
static void lok_wake_callback(void *)
static void reportError(LOKDocView *, const std::string &rString)
Definition: lokdocview.cxx:816
SAL_DLLPUBLIC_EXPORT gfloat lok_doc_view_twip_to_pixel(LOKDocView *pDocView, float fInput)
static gboolean lok_doc_view_signal_button(GtkWidget *pWidget, GdkEventButton *pEvent)
static void commandChanged(LOKDocView *pDocView, const std::string &rString)
Definition: lokdocview.cxx:785
static gboolean lok_doc_view_signal_motion(GtkWidget *pWidget, GdkEventMotion *pEvent)
static void formulaChanged(LOKDocView *pDocView, const std::string &rString)
Definition: lokdocview.cxx:811
static void setEditInThread(gpointer data)
static void lok_doc_view_init(LOKDocView *pDocView)
static void postCommandInThread(gpointer data)
static guint doc_view_signals[LAST_SIGNAL]
Definition: lokdocview.cxx:334
SAL_DLLPUBLIC_EXPORT gint lok_doc_view_get_parts(LOKDocView *pDocView)
#define GRAPHIC_HANDLE_COUNT
Definition: lokdocview.cxx:41
SAL_DLLPUBLIC_EXPORT void lok_doc_view_find_prev(LOKDocView *pDocView, const gchar *pText, gboolean bHighlightAll)
SAL_DLLPUBLIC_EXPORT void lok_doc_view_highlight_all(LOKDocView *pDocView, const gchar *pText)
static bool handleGraphicSelectionOnButtonRelease(LOKDocView *pDocView, GdkEventButton *pEvent)
if handled, returns TRUE else FALSE
Definition: lokdocview.cxx:542
#define MAX_ZOOM
Definition: lokdocview.cxx:43
static void renderHandle(LOKDocView *pDocView, cairo_t *pCairo, const GdkRectangle &rCursor, cairo_surface_t *pHandle, GdkRectangle &rRectangle)
static void doSearch(LOKDocView *pDocView, const char *pText, bool bBackwards, bool highlightAll)
Definition: lokdocview.cxx:405
static void updateClientZoom(LOKDocView *pDocView)
static void commandResult(LOKDocView *pDocView, const std::string &rString)
Definition: lokdocview.cxx:801
SAL_DLLPUBLIC_EXPORT LibreOfficeKitDocument * lok_doc_view_get_document(LOKDocView *pDocView)
#define MIN_ZOOM
Definition: lokdocview.cxx:45
SAL_DLLPUBLIC_EXPORT gchar * lok_doc_view_get_version_info(LOKDocView *pDocView)
static bool renderDocument(LOKDocView *pDocView, cairo_t *pCairo)
static void searchResultCount(LOKDocView *pDocView, const std::string &rString)
Definition: lokdocview.cxx:796
static void lok_doc_view_finalize(GObject *object)
#define G_SOURCE_REMOVE
Definition: lokdocview.cxx:31
static void getDragPoint(GdkRectangle *pHandle, GdkEventMotion *pEvent, GdkPoint *pPoint)
static void lok_doc_view_destroy(GtkWidget *widget)
static void lok_doc_view_class_init(LOKDocViewClass *pClass)
static void LOKPostCommand(LOKDocView *pDocView, const gchar *pCommand, const gchar *pArguments, bool bNotifyWhenFinished)
Definition: lokdocview.cxx:381
SAL_DLLPUBLIC_EXPORT void lok_doc_view_set_part(LOKDocView *pDocView, int nPart)
SAL_DLLPUBLIC_EXPORT void lok_doc_view_find_next(LOKDocView *pDocView, const gchar *pText, gboolean bHighlightAll)
static void searchNotFound(LOKDocView *pDocView, const std::string &rString)
Definition: lokdocview.cxx:791
static std::map< std::string, int > g_aAuthorViews
Author string <-> View ID map.
Definition: lokdocview.cxx:872
#define CURSOR_HANDLE_DIR
Definition: lokdocview.cxx:39
static void setPart(LOKDocView *pDocView, const std::string &rString)
Definition: lokdocview.cxx:829
SAL_DLLPUBLIC_EXPORT gboolean lok_doc_view_open_document_finish(LOKDocView *pDocView, GAsyncResult *res, GError **error)
SAL_DLLPUBLIC_EXPORT GtkWidget * lok_doc_view_new(const gchar *pPath, GCancellable *cancellable, GError **error)
static gboolean queueDraw(gpointer pData)
Trigger a redraw, invoked on the main thread by other functions running in a thread.
Definition: lokdocview.cxx:843
SAL_DLLPUBLIC_EXPORT GtkWidget * lok_doc_view_new_from_widget(LOKDocView *pOldLOKDocView, const gchar *pRenderingArguments)
static void hyperlinkClicked(LOKDocView *pDocView, const std::string &rString)
Definition: lokdocview.cxx:837
static void lok_doc_view_get_property(GObject *object, guint propId, GValue *value, GParamSpec *pspec)
SAL_DLLPUBLIC_EXPORT void lok_doc_view_send_content_control_event(LOKDocView *pDocView, const gchar *pArguments)
SAL_DLLPUBLIC_EXPORT gchar * lok_doc_view_get_command_values(LOKDocView *pDocView, const gchar *pCommand)
static void globalCallbackWorker(int nType, const char *pPayload, void *pData)
Definition: lokdocview.cxx:983
static void setClientZoomInThread(gpointer data)
static void lok_doc_view_set_property(GObject *object, guint propId, const GValue *value, GParamSpec *pspec)
SAL_DLLPUBLIC_EXPORT gboolean lok_doc_view_get_edit(LOKDocView *pDocView)
static gboolean globalCallback(gpointer pData)
Implementation of the global callback handler, invoked by globalCallback();.
Definition: lokdocview.cxx:928
SAL_DLLPUBLIC_EXPORT gfloat lok_doc_view_get_zoom(LOKDocView *pDocView)
static void lok_doc_view_initable_iface_init(GInitableIface *iface)
static std::mutex g_aLOKMutex
This is expected to be locked during setView(), doSomethingElse() LOK calls.
Definition: lokdocview.cxx:48
static gboolean lok_doc_view_initable_init(GInitable *initable, GCancellable *, GError **error)
static gboolean callback(gpointer pData)
SAL_DLLPUBLIC_EXPORT void lok_doc_view_set_document_password(LOKDocView *pDocView, const gchar *pURL, const gchar *pPassword)
static void callbackWorker(int nType, const char *pPayload, void *pData)
static void setPartInThread(gpointer data)
SAL_DLLPUBLIC_EXPORT GtkWidget * lok_doc_view_new_from_user_profile(const gchar *pPath, const gchar *pUserProfile, GCancellable *cancellable, GError **error)
static void onStyleContextChanged(LOKDocView *pDocView)
static void addressChanged(LOKDocView *pDocView, const std::string &rString)
Definition: lokdocview.cxx:806
static gboolean timeout_wakeup(void *)
static void setPartmodeInThread(gpointer data)
SAL_DLLPUBLIC_EXPORT void lok_doc_view_set_edit(LOKDocView *pDocView, gboolean bEdit)
SAL_DLLPUBLIC_EXPORT void lok_doc_view_set_visible_area(LOKDocView *pDocView, GdkRectangle *pVisibleArea)
static std::vector< GdkRectangle > payloadToRectangles(LOKDocView *pDocView, const char *pPayload)
static void postKeyEventInThread(gpointer data)
Definition: lokdocview.cxx:597
static void paintTileInThread(gpointer data)
SAL_DLLPUBLIC_EXPORT gchar * lok_doc_view_copy_selection(LOKDocView *pDocView, const gchar *pMimeType, gchar **pUsedMimeType)
@ PROP_DOC_PATH
Definition: lokdocview.cxx:316
@ PROP_DOC_PASSWORD
Definition: lokdocview.cxx:327
@ PROP_LO_POINTER
Definition: lokdocview.cxx:314
@ PROP_LO_PATH
Definition: lokdocview.cxx:312
@ PROP_EDITABLE
Definition: lokdocview.cxx:318
@ PROP_LOAD_PROGRESS
Definition: lokdocview.cxx:319
@ PROP_DOC_HEIGHT
Definition: lokdocview.cxx:324
@ PROP_ZOOM
Definition: lokdocview.cxx:320
@ PROP_TILED_ANNOTATIONS
Definition: lokdocview.cxx:329
@ PROP_DOC_WIDTH
Definition: lokdocview.cxx:323
@ PROP_LO_UNIPOLL
Definition: lokdocview.cxx:313
@ PROP_LAST
Definition: lokdocview.cxx:331
@ PROP_DOC_PASSWORD_TO_MODIFY
Definition: lokdocview.cxx:328
@ PROP_CAN_ZOOM_IN
Definition: lokdocview.cxx:325
@ PROP_0
Definition: lokdocview.cxx:310
@ PROP_IS_INITIALIZED
Definition: lokdocview.cxx:322
@ PROP_DOC_POINTER
Definition: lokdocview.cxx:317
@ PROP_IS_LOADING
Definition: lokdocview.cxx:321
@ PROP_USER_PROFILE_URL
Definition: lokdocview.cxx:315
@ PROP_CAN_ZOOM_OUT
Definition: lokdocview.cxx:326
static gboolean handleTimeout(gpointer pData)
Definition: lokdocview.cxx:767
static gboolean spin_lok_loop(void *pData)
SAL_DLLPUBLIC_EXPORT void lok_doc_view_set_zoom(LOKDocView *pDocView, float fZoom)
SAL_DLLPUBLIC_EXPORT gchar * lok_doc_view_get_part_name(LOKDocView *pDocView, int nPart)
SAL_DLLPUBLIC_EXPORT gfloat lok_doc_view_pixel_to_twip(LOKDocView *pDocView, float fInput)
static LOKDocViewPrivate & getPrivate(LOKDocView *pDocView)
Definition: lokdocview.cxx:357
static bool isEmptyRectangle(const GdkRectangle &rRectangle)
Definition: lokdocview.cxx:446
SAL_DLLPUBLIC_EXPORT gboolean lok_doc_view_paste(LOKDocView *pDocView, const gchar *pMimeType, const gchar *pData, gsize nSize)
SAL_DLLPUBLIC_EXPORT gint lok_doc_view_get_part(LOKDocView *pDocView)
#define G_SOURCE_CONTINUE
Definition: lokdocview.cxx:32
static void paintTileCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData)
Callback called in the main UI thread when paintTileInThread in LOK thread has finished.
static bool renderOverlay(LOKDocView *pDocView, cairo_t *pCairo)
static const GdkRGBA & getDarkColor(int nViewId, LOKDocViewPrivate &priv)
SAL_DLLPUBLIC_EXPORT void lok_doc_view_reset_view(LOKDocView *pDocView)
static gpointer paintTileFinish(LOKDocView *pDocView, GAsyncResult *res, GError **error)
Finishes the paint tile operation and returns the result, if any.
std::unique_ptr< sal_Int32[]> pData
int i
QPRO_FUNC_TYPE nType
A struct that we use to store the data about the LOK call.
Definition: tilebuffer.hxx:183
float m_fPaintTileZoom
Definition: tilebuffer.hxx:214
int m_nSetGraphicSelectionX
Definition: tilebuffer.hxx:231
int m_nPostMouseEventY
Definition: tilebuffer.hxx:222
gchar * m_pArguments
Definition: tilebuffer.hxx:190
int m_nTilePixelHeight
Definition: tilebuffer.hxx:238
int m_nPostMouseEventButton
Definition: tilebuffer.hxx:224
static void destroy(void *pMemory)
Wrapper around delete to help GLib.
Definition: tilebuffer.cxx:130
int m_nPaintTileY
Definition: tilebuffer.hxx:213
gboolean m_bEdit
set_edit parameter
Definition: tilebuffer.hxx:195
int m_nPaintTileX
Definition: tilebuffer.hxx:212
int m_nTileTwipWidth
Definition: tilebuffer.hxx:239
int m_nCharCode
Definition: tilebuffer.hxx:206
int m_nTilePixelWidth
Definition: tilebuffer.hxx:237
int m_nTileTwipHeight
Definition: tilebuffer.hxx:240
int m_nKeyEvent
Definition: tilebuffer.hxx:205
int m_nPartMode
set_partmode parameter
Definition: tilebuffer.hxx:198
int m_nPostMouseEventX
Definition: tilebuffer.hxx:221
int m_nPart
set_part parameter
Definition: tilebuffer.hxx:201
TileBuffer * m_pTileBuffer
Definition: tilebuffer.hxx:215
int m_nSetGraphicSelectionType
Definition: tilebuffer.hxx:230
int m_nSetGraphicSelectionY
Definition: tilebuffer.hxx:232
int m_nPostMouseEventModifier
Definition: tilebuffer.hxx:225
int m_nPostMouseEventType
Definition: tilebuffer.hxx:220
int m_nType
To identify the type of LOK call.
Definition: tilebuffer.hxx:185
int m_nKeyCode
Definition: tilebuffer.hxx:207
gboolean m_bNotifyWhenFinished
Definition: tilebuffer.hxx:191
const gchar * m_pCommand
Definition: tilebuffer.hxx:189
int m_nPostMouseEventCount
Definition: tilebuffer.hxx:223
Wrapper around LOKDocViewPrivateImpl, managed by malloc/memset/free.
Definition: lokdocview.cxx:274
LOKDocViewPrivateImpl * operator->()
Definition: lokdocview.cxx:277
LOKDocViewPrivateImpl * m_pImpl
Definition: lokdocview.cxx:275
float pixelToTwip(float fInput, float zoom)
Converts the pixel value to zoom independent twip value.
Definition: tilebuffer.cxx:20
float twipToPixel(float fInput, float zoom)
Converts the zoom independent twip value pixel value.
Definition: tilebuffer.cxx:25
#define LOK_TILEBUFFER_ERROR
Definition: tilebuffer.hxx:19
const int nTileSizePixels
Definition: tilebuffer.hxx:22
@ LOK_POST_COMMAND
Definition: tilebuffer.hxx:158
@ LOK_POST_MOUSE_EVENT
Definition: tilebuffer.hxx:164
@ LOK_SET_PART
Definition: tilebuffer.hxx:161
@ LOK_LOAD_DOC
Definition: tilebuffer.hxx:157
@ LOK_SET_CLIENT_ZOOM
Definition: tilebuffer.hxx:166
@ LOK_SET_EDIT
Definition: tilebuffer.hxx:159
@ LOK_POST_KEY
Definition: tilebuffer.hxx:162
@ LOK_SET_GRAPHIC_SELECTION
Definition: tilebuffer.hxx:165
@ LOK_PAINT_TILE
Definition: tilebuffer.hxx:163
@ LOK_SET_PARTMODE
Definition: tilebuffer.hxx:160
@ LOK_TILEBUFFER_CHANGED
Definition: tilebuffer.hxx:171
@ LOK_TILEBUFFER_MEMORY
Definition: tilebuffer.hxx:172
unsigned char sal_uInt8