Version 5.2.6.1, tag libreoffice-5.2.6.1
[LibreOffice.git] / libreofficekit / source / gtk / lokdocview.cxx
blob38b1a6dc78b4bb746502beacb17987b3de42bd0e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <sal/types.h>
11 #include <math.h>
12 #include <string.h>
13 #include <memory>
14 #include <vector>
15 #include <string>
16 #include <sstream>
17 #include <iostream>
18 #include <boost/property_tree/json_parser.hpp>
20 #include <com/sun/star/awt/Key.hpp>
21 #include <LibreOfficeKit/LibreOfficeKit.h>
22 #include <LibreOfficeKit/LibreOfficeKitInit.h>
23 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
24 #include <LibreOfficeKit/LibreOfficeKitGtk.h>
25 #include <rsc/rsc-vcl-shared-types.hxx>
26 #include <vcl/event.hxx>
28 #include "tilebuffer.hxx"
30 #if !GLIB_CHECK_VERSION(2,32,0)
31 #define G_SOURCE_REMOVE FALSE
32 #define G_SOURCE_CONTINUE TRUE
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
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
47 /// Private struct used by this GObject type
48 struct LOKDocViewPrivateImpl
50 const gchar* m_aLOPath;
51 const gchar* m_pUserProfileURL;
52 const gchar* m_aDocPath;
53 std::string m_aRenderingArguments;
54 gdouble m_nLoadProgress;
55 gboolean m_bIsLoading;
56 gboolean m_bCanZoomIn;
57 gboolean m_bCanZoomOut;
58 LibreOfficeKit* m_pOffice;
59 LibreOfficeKitDocument* m_pDocument;
61 std::unique_ptr<TileBuffer> m_pTileBuffer;
62 GThreadPool* lokThreadPool;
64 gfloat m_fZoom;
65 glong m_nDocumentWidthTwips;
66 glong m_nDocumentHeightTwips;
67 /// View or edit mode.
68 gboolean m_bEdit;
69 /// LOK Features
70 guint64 m_nLOKFeatures;
71 /// Number of parts in currently loaded document
72 gint m_nParts;
73 /// Position and size of the visible cursor.
74 GdkRectangle m_aVisibleCursor;
75 /// Cursor overlay is visible or hidden (for blinking).
76 gboolean m_bCursorOverlayVisible;
77 /// Cursor is visible or hidden (e.g. for graphic selection).
78 gboolean m_bCursorVisible;
79 /// Time of the last button press.
80 guint32 m_nLastButtonPressTime;
81 /// Time of the last button release.
82 guint32 m_nLastButtonReleaseTime;
83 /// Last pressed button (left, right, middle)
84 guint32 m_nLastButtonPressed;
85 /// Key modifier (ctrl, atl, shift)
86 guint32 m_nKeyModifier;
87 /// Rectangles of the current text selection.
88 std::vector<GdkRectangle> m_aTextSelectionRectangles;
89 /// Position and size of the selection start (as if there would be a cursor caret there).
90 GdkRectangle m_aTextSelectionStart;
91 /// Position and size of the selection end.
92 GdkRectangle m_aTextSelectionEnd;
93 GdkRectangle m_aGraphicSelection;
94 GdkRectangle m_aCellCursor;
95 gboolean m_bInDragGraphicSelection;
97 /// @name Start/middle/end handle.
98 ///@{
99 /// Bitmap of the text selection start handle.
100 cairo_surface_t* m_pHandleStart;
101 /// Rectangle of the text selection start handle, to know if the user clicked on it or not
102 GdkRectangle m_aHandleStartRect;
103 /// If we are in the middle of a drag of the text selection end handle.
104 gboolean m_bInDragStartHandle;
105 /// Bitmap of the text selection middle handle.
106 cairo_surface_t* m_pHandleMiddle;
107 /// Rectangle of the text selection middle handle, to know if the user clicked on it or not
108 GdkRectangle m_aHandleMiddleRect;
109 /// If we are in the middle of a drag of the text selection middle handle.
110 gboolean m_bInDragMiddleHandle;
111 /// Bitmap of the text selection end handle.
112 cairo_surface_t* m_pHandleEnd;
113 /// Rectangle of the text selection end handle, to know if the user clicked on it or not
114 GdkRectangle m_aHandleEndRect;
115 /// If we are in the middle of a drag of the text selection end handle.
116 gboolean m_bInDragEndHandle;
117 ///@}
119 /// @name Graphic handles.
120 ///@{
121 /// Bitmap of a graphic selection handle.
122 cairo_surface_t* m_pGraphicHandle;
123 /// Rectangle of a graphic selection handle, to know if the user clicked on it or not.
124 GdkRectangle m_aGraphicHandleRects[8];
125 /// If we are in the middle of a drag of a graphic selection handle.
126 gboolean m_bInDragGraphicHandles[8];
127 ///@}
129 /// View ID, returned by createView() or 0 by default.
130 int m_nViewId;
133 * Contains a freshly set zoom level: logic size of a tile.
134 * It gets reset back to 0 when LOK was informed about this zoom change.
136 int m_nTileSizeTwips;
138 GdkRectangle m_aVisibleArea;
139 bool m_bVisibleAreaSet;
141 LOKDocViewPrivateImpl()
142 : m_aLOPath(nullptr),
143 m_pUserProfileURL(nullptr),
144 m_aDocPath(nullptr),
145 m_nLoadProgress(0),
146 m_bIsLoading(false),
147 m_bCanZoomIn(true),
148 m_bCanZoomOut(true),
149 m_pOffice(nullptr),
150 m_pDocument(nullptr),
151 lokThreadPool(nullptr),
152 m_fZoom(0),
153 m_nDocumentWidthTwips(0),
154 m_nDocumentHeightTwips(0),
155 m_bEdit(FALSE),
156 m_nLOKFeatures(0),
157 m_nParts(0),
158 m_aVisibleCursor({0, 0, 0, 0}),
159 m_bCursorOverlayVisible(false),
160 m_bCursorVisible(true),
161 m_nLastButtonPressTime(0),
162 m_nLastButtonReleaseTime(0),
163 m_nLastButtonPressed(0),
164 m_nKeyModifier(0),
165 m_aTextSelectionStart({0, 0, 0, 0}),
166 m_aTextSelectionEnd({0, 0, 0, 0}),
167 m_aGraphicSelection({0, 0, 0, 0}),
168 m_aCellCursor({0, 0, 0, 0}),
169 m_bInDragGraphicSelection(false),
170 m_pHandleStart(nullptr),
171 m_aHandleStartRect({0, 0, 0, 0}),
172 m_bInDragStartHandle(0),
173 m_pHandleMiddle(nullptr),
174 m_aHandleMiddleRect({0, 0, 0, 0}),
175 m_bInDragMiddleHandle(false),
176 m_pHandleEnd(nullptr),
177 m_aHandleEndRect({0, 0, 0, 0}),
178 m_bInDragEndHandle(false),
179 m_pGraphicHandle(nullptr),
180 m_nViewId(0),
181 m_nTileSizeTwips(0),
182 m_aVisibleArea({0, 0, 0, 0}),
183 m_bVisibleAreaSet(false)
185 memset(&m_aGraphicHandleRects, 0, sizeof(m_aGraphicHandleRects));
186 memset(&m_bInDragGraphicHandles, 0, sizeof(m_bInDragGraphicHandles));
190 /// Wrapper around LOKDocViewPrivateImpl, managed by malloc/memset/free.
191 struct _LOKDocViewPrivate
193 LOKDocViewPrivateImpl* m_pImpl;
195 LOKDocViewPrivateImpl* operator->()
197 return m_pImpl;
201 enum
203 LOAD_CHANGED,
204 EDIT_CHANGED,
205 COMMAND_CHANGED,
206 SEARCH_NOT_FOUND,
207 PART_CHANGED,
208 SIZE_CHANGED,
209 HYPERLINK_CLICKED,
210 CURSOR_CHANGED,
211 SEARCH_RESULT_COUNT,
212 COMMAND_RESULT,
213 FORMULA_CHANGED,
214 TEXT_SELECTION,
215 PASSWORD_REQUIRED,
217 LAST_SIGNAL
220 enum
222 PROP_0,
224 PROP_LO_PATH,
225 PROP_LO_POINTER,
226 PROP_USER_PROFILE_URL,
227 PROP_DOC_PATH,
228 PROP_DOC_POINTER,
229 PROP_EDITABLE,
230 PROP_LOAD_PROGRESS,
231 PROP_ZOOM,
232 PROP_IS_LOADING,
233 PROP_DOC_WIDTH,
234 PROP_DOC_HEIGHT,
235 PROP_CAN_ZOOM_IN,
236 PROP_CAN_ZOOM_OUT,
237 PROP_DOC_PASSWORD,
238 PROP_DOC_PASSWORD_TO_MODIFY,
240 PROP_LAST
243 static guint doc_view_signals[LAST_SIGNAL] = { 0 };
244 static GParamSpec *properties[PROP_LAST] = { nullptr };
246 static void lok_doc_view_initable_iface_init (GInitableIface *iface);
247 static void callbackWorker (int nType, const char* pPayload, void* pData);
249 SAL_DLLPUBLIC_EXPORT GType lok_doc_view_get_type();
250 #ifdef __GNUC__
251 #pragma GCC diagnostic push
252 #pragma GCC diagnostic ignored "-Wunused-function"
253 #endif
254 G_DEFINE_TYPE_WITH_CODE (LOKDocView, lok_doc_view, GTK_TYPE_DRAWING_AREA,
255 G_ADD_PRIVATE (LOKDocView)
256 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, lok_doc_view_initable_iface_init));
257 #ifdef __GNUC__
258 #pragma GCC diagnostic pop
259 #endif
261 static LOKDocViewPrivate& getPrivate(LOKDocView* pDocView)
263 LOKDocViewPrivate* priv = static_cast<LOKDocViewPrivate*>(lok_doc_view_get_instance_private(pDocView));
264 return *priv;
267 /// Helper struct used to pass the data from soffice thread -> main thread.
268 struct CallbackData
270 int m_nType;
271 std::string m_aPayload;
272 LOKDocView* m_pDocView;
274 CallbackData(int nType, const std::string& rPayload, LOKDocView* pDocView)
275 : m_nType(nType),
276 m_aPayload(rPayload),
277 m_pDocView(pDocView) {}
280 static void
281 payloadToSize(const char* pPayload, long& rWidth, long& rHeight)
283 rWidth = rHeight = 0;
284 gchar** ppCoordinates = g_strsplit(pPayload, ", ", 2);
285 gchar** ppCoordinate = ppCoordinates;
286 if (!*ppCoordinate)
287 return;
288 rWidth = atoi(*ppCoordinate);
289 ++ppCoordinate;
290 if (!*ppCoordinate)
291 return;
292 rHeight = atoi(*ppCoordinate);
293 g_strfreev(ppCoordinates);
296 /// Returns the string representation of a LibreOfficeKitCallbackType enumeration element.
297 static const char*
298 callbackTypeToString (int nType)
300 switch (nType)
302 case LOK_CALLBACK_INVALIDATE_TILES:
303 return "LOK_CALLBACK_INVALIDATE_TILES";
304 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
305 return "LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR";
306 case LOK_CALLBACK_TEXT_SELECTION:
307 return "LOK_CALLBACK_TEXT_SELECTION";
308 case LOK_CALLBACK_TEXT_SELECTION_START:
309 return "LOK_CALLBACK_TEXT_SELECTION_START";
310 case LOK_CALLBACK_TEXT_SELECTION_END:
311 return "LOK_CALLBACK_TEXT_SELECTION_END";
312 case LOK_CALLBACK_CURSOR_VISIBLE:
313 return "LOK_CALLBACK_CURSOR_VISIBLE";
314 case LOK_CALLBACK_GRAPHIC_SELECTION:
315 return "LOK_CALLBACK_GRAPHIC_SELECTION";
316 case LOK_CALLBACK_CELL_CURSOR:
317 return "LOK_CALLBACK_CELL_CURSOR";
318 case LOK_CALLBACK_HYPERLINK_CLICKED:
319 return "LOK_CALLBACK_HYPERLINK_CLICKED";
320 case LOK_CALLBACK_MOUSE_POINTER:
321 return "LOK_CALLBACK_MOUSE_POINTER";
322 case LOK_CALLBACK_STATE_CHANGED:
323 return "LOK_CALLBACK_STATE_CHANGED";
324 case LOK_CALLBACK_STATUS_INDICATOR_START:
325 return "LOK_CALLBACK_STATUS_INDICATOR_START";
326 case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE:
327 return "LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE";
328 case LOK_CALLBACK_STATUS_INDICATOR_FINISH:
329 return "LOK_CALLBACK_STATUS_INDICATOR_FINISH";
330 case LOK_CALLBACK_SEARCH_NOT_FOUND:
331 return "LOK_CALLBACK_SEARCH_NOT_FOUND";
332 case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED:
333 return "LOK_CALLBACK_DOCUMENT_SIZE_CHANGED";
334 case LOK_CALLBACK_SET_PART:
335 return "LOK_CALLBACK_SET_PART";
336 case LOK_CALLBACK_SEARCH_RESULT_SELECTION:
337 return "LOK_CALLBACK_SEARCH_RESULT_SELECTION";
338 case LOK_CALLBACK_DOCUMENT_PASSWORD:
339 return "LOK_CALLBACK_DOCUMENT_PASSWORD";
340 case LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY:
341 return "LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY";
342 case LOK_CALLBACK_CONTEXT_MENU:
343 return "LOK_CALLBACK_CONTEXT_MENU";
345 return nullptr;
348 static void
349 LOKPostCommand (LOKDocView* pDocView,
350 const gchar* pCommand,
351 const gchar* pArguments,
352 gboolean bNotifyWhenFinished)
354 LOKDocViewPrivate& priv = getPrivate(pDocView);
355 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
356 LOEvent* pLOEvent = new LOEvent(LOK_POST_COMMAND);
357 GError* error = nullptr;
358 pLOEvent->m_pCommand = pCommand;
359 pLOEvent->m_pArguments = g_strdup(pArguments);
360 pLOEvent->m_bNotifyWhenFinished = bNotifyWhenFinished;
362 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
363 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
364 if (error != nullptr)
366 g_warning("Unable to call LOK_POST_COMMAND: %s", error->message);
367 g_clear_error(&error);
369 g_object_unref(task);
372 static void
373 doSearch(LOKDocView* pDocView, const char* pText, bool bBackwards, bool highlightAll)
375 LOKDocViewPrivate& priv = getPrivate(pDocView);
376 if (!priv->m_pDocument)
377 return;
379 boost::property_tree::ptree aTree;
380 GtkWidget* drawingWidget = GTK_WIDGET(pDocView);
381 GdkWindow* drawingWindow = gtk_widget_get_window(drawingWidget);
382 if (!drawingWindow)
383 return;
384 std::shared_ptr<cairo_region_t> cairoVisRegion( gdk_window_get_visible_region(drawingWindow),
385 cairo_region_destroy);
386 cairo_rectangle_int_t cairoVisRect;
387 cairo_region_get_rectangle(cairoVisRegion.get(), 0, &cairoVisRect);
388 int x = pixelToTwip (cairoVisRect.x, priv->m_fZoom);
389 int y = pixelToTwip (cairoVisRect.y, priv->m_fZoom);
391 aTree.put(boost::property_tree::ptree::path_type("SearchItem.SearchString/type", '/'), "string");
392 aTree.put(boost::property_tree::ptree::path_type("SearchItem.SearchString/value", '/'), pText);
393 aTree.put(boost::property_tree::ptree::path_type("SearchItem.Backward/type", '/'), "boolean");
394 aTree.put(boost::property_tree::ptree::path_type("SearchItem.Backward/value", '/'), bBackwards);
395 if (highlightAll)
397 aTree.put(boost::property_tree::ptree::path_type("SearchItem.Command/type", '/'), "unsigned short");
398 // SvxSearchCmd::FIND_ALL
399 aTree.put(boost::property_tree::ptree::path_type("SearchItem.Command/value", '/'), "1");
402 aTree.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointX/type", '/'), "long");
403 aTree.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointX/value", '/'), x);
404 aTree.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointY/type", '/'), "long");
405 aTree.put(boost::property_tree::ptree::path_type("SearchItem.SearchStartPointY/value", '/'), y);
407 std::stringstream aStream;
408 boost::property_tree::write_json(aStream, aTree);
410 LOKPostCommand (pDocView, ".uno:ExecuteSearch", aStream.str().c_str(), false);
413 static bool
414 isEmptyRectangle(const GdkRectangle& rRectangle)
416 return rRectangle.x == 0 && rRectangle.y == 0 && rRectangle.width == 0 && rRectangle.height == 0;
419 /// if handled, returns TRUE else FALSE
420 static bool
421 handleTextSelectionOnButtonPress(GdkRectangle& aClick, LOKDocView* pDocView) {
422 LOKDocViewPrivate& priv = getPrivate(pDocView);
424 if (gdk_rectangle_intersect(&aClick, &priv->m_aHandleStartRect, nullptr))
426 g_info("LOKDocView_Impl::signalButton: start of drag start handle");
427 priv->m_bInDragStartHandle = true;
428 return TRUE;
430 else if (gdk_rectangle_intersect(&aClick, &priv->m_aHandleMiddleRect, nullptr))
432 g_info("LOKDocView_Impl::signalButton: start of drag middle handle");
433 priv->m_bInDragMiddleHandle = true;
434 return TRUE;
436 else if (gdk_rectangle_intersect(&aClick, &priv->m_aHandleEndRect, nullptr))
438 g_info("LOKDocView_Impl::signalButton: start of drag end handle");
439 priv->m_bInDragEndHandle = true;
440 return TRUE;
443 return FALSE;
446 /// if handled, returns TRUE else FALSE
447 static bool
448 handleGraphicSelectionOnButtonPress(GdkRectangle& aClick, LOKDocView* pDocView) {
449 LOKDocViewPrivate& priv = getPrivate(pDocView);
450 GError* error = nullptr;
452 for (int i = 0; i < GRAPHIC_HANDLE_COUNT; ++i)
454 if (gdk_rectangle_intersect(&aClick, &priv->m_aGraphicHandleRects[i], nullptr))
456 g_info("LOKDocView_Impl::signalButton: start of drag graphic handle #%d", i);
457 priv->m_bInDragGraphicHandles[i] = true;
459 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
460 LOEvent* pLOEvent = new LOEvent(LOK_SET_GRAPHIC_SELECTION);
461 pLOEvent->m_nSetGraphicSelectionType = LOK_SETGRAPHICSELECTION_START;
462 pLOEvent->m_nSetGraphicSelectionX = pixelToTwip(priv->m_aGraphicHandleRects[i].x + priv->m_aGraphicHandleRects[i].width / 2, priv->m_fZoom);
463 pLOEvent->m_nSetGraphicSelectionY = pixelToTwip(priv->m_aGraphicHandleRects[i].y + priv->m_aGraphicHandleRects[i].height / 2, priv->m_fZoom);
464 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
466 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
467 if (error != nullptr)
469 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error->message);
470 g_clear_error(&error);
472 g_object_unref(task);
474 return TRUE;
478 return FALSE;
481 /// if handled, returns TRUE else FALSE
482 static bool
483 handleTextSelectionOnButtonRelease(LOKDocView* pDocView) {
484 LOKDocViewPrivate& priv = getPrivate(pDocView);
486 if (priv->m_bInDragStartHandle)
488 g_info("LOKDocView_Impl::signalButton: end of drag start handle");
489 priv->m_bInDragStartHandle = false;
490 return TRUE;
492 else if (priv->m_bInDragMiddleHandle)
494 g_info("LOKDocView_Impl::signalButton: end of drag middle handle");
495 priv->m_bInDragMiddleHandle = false;
496 return TRUE;
498 else if (priv->m_bInDragEndHandle)
500 g_info("LOKDocView_Impl::signalButton: end of drag end handle");
501 priv->m_bInDragEndHandle = false;
502 return TRUE;
505 return FALSE;
508 /// if handled, returns TRUE else FALSE
509 static bool
510 handleGraphicSelectionOnButtonRelease(LOKDocView* pDocView, GdkEventButton* pEvent) {
511 LOKDocViewPrivate& priv = getPrivate(pDocView);
512 GError* error = nullptr;
514 for (int i = 0; i < GRAPHIC_HANDLE_COUNT; ++i)
516 if (priv->m_bInDragGraphicHandles[i])
518 g_info("LOKDocView_Impl::signalButton: end of drag graphic handle #%d", i);
519 priv->m_bInDragGraphicHandles[i] = false;
521 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
522 LOEvent* pLOEvent = new LOEvent(LOK_SET_GRAPHIC_SELECTION);
523 pLOEvent->m_nSetGraphicSelectionType = LOK_SETGRAPHICSELECTION_END;
524 pLOEvent->m_nSetGraphicSelectionX = pixelToTwip(pEvent->x, priv->m_fZoom);
525 pLOEvent->m_nSetGraphicSelectionY = pixelToTwip(pEvent->y, priv->m_fZoom);
526 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
528 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
529 if (error != nullptr)
531 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error->message);
532 g_clear_error(&error);
534 g_object_unref(task);
536 return TRUE;
540 if (priv->m_bInDragGraphicSelection)
542 g_info("LOKDocView_Impl::signalButton: end of drag graphic selection");
543 priv->m_bInDragGraphicSelection = false;
545 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
546 LOEvent* pLOEvent = new LOEvent(LOK_SET_GRAPHIC_SELECTION);
547 pLOEvent->m_nSetGraphicSelectionType = LOK_SETGRAPHICSELECTION_END;
548 pLOEvent->m_nSetGraphicSelectionX = pixelToTwip(pEvent->x, priv->m_fZoom);
549 pLOEvent->m_nSetGraphicSelectionY = pixelToTwip(pEvent->y, priv->m_fZoom);
550 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
552 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
553 if (error != nullptr)
555 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error->message);
556 g_clear_error(&error);
558 g_object_unref(task);
560 return TRUE;
563 return FALSE;
566 static void
567 postKeyEventInThread(gpointer data)
569 GTask* task = G_TASK(data);
570 LOKDocView* pDocView = LOK_DOC_VIEW(g_task_get_source_object(task));
571 LOKDocViewPrivate& priv = getPrivate(pDocView);
572 LOEvent* pLOEvent = static_cast<LOEvent*>(g_task_get_task_data(task));
574 priv->m_pDocument->pClass->setView(priv->m_pDocument, priv->m_nViewId);
576 if (priv->m_nTileSizeTwips)
578 std::stringstream ss;
579 ss << "lok::Document::setClientZoom(" << nTileSizePixels << ", " << nTileSizePixels << ", " << priv->m_nTileSizeTwips << ", " << priv->m_nTileSizeTwips << ")";
580 g_info("%s", ss.str().c_str());
581 priv->m_pDocument->pClass->setClientZoom(priv->m_pDocument,
582 nTileSizePixels,
583 nTileSizePixels,
584 priv->m_nTileSizeTwips,
585 priv->m_nTileSizeTwips);
586 priv->m_nTileSizeTwips = 0;
588 if (priv->m_bVisibleAreaSet)
590 std::stringstream ss;
591 ss << "lok::Document::setClientVisibleArea(" << priv->m_aVisibleArea.x << ", " << priv->m_aVisibleArea.y << ", ";
592 ss << priv->m_aVisibleArea.width << ", " << priv->m_aVisibleArea.height << ")";
593 g_info("%s", ss.str().c_str());
594 priv->m_pDocument->pClass->setClientVisibleArea(priv->m_pDocument,
595 priv->m_aVisibleArea.x,
596 priv->m_aVisibleArea.y,
597 priv->m_aVisibleArea.width,
598 priv->m_aVisibleArea.height);
599 priv->m_bVisibleAreaSet = false;
602 std::stringstream ss;
603 ss << "lok::Document::postKeyEvent(" << pLOEvent->m_nKeyEvent << ", " << pLOEvent->m_nCharCode << ", " << pLOEvent->m_nKeyCode << ")";
604 g_info("%s", ss.str().c_str());
605 priv->m_pDocument->pClass->postKeyEvent(priv->m_pDocument,
606 pLOEvent->m_nKeyEvent,
607 pLOEvent->m_nCharCode,
608 pLOEvent->m_nKeyCode);
611 static gboolean
612 signalKey (GtkWidget* pWidget, GdkEventKey* pEvent)
614 LOKDocView* pDocView = LOK_DOC_VIEW(pWidget);
615 LOKDocViewPrivate& priv = getPrivate(pDocView);
616 int nCharCode = 0;
617 int nKeyCode = 0;
618 GError* error = nullptr;
620 if (!priv->m_bEdit)
622 g_info("signalKey: not in edit mode, ignore");
623 return FALSE;
626 priv->m_nKeyModifier &= KEY_MOD2;
627 switch (pEvent->keyval)
629 case GDK_KEY_BackSpace:
630 nKeyCode = com::sun::star::awt::Key::BACKSPACE;
631 break;
632 case GDK_KEY_Delete:
633 nKeyCode = com::sun::star::awt::Key::DELETE;
634 break;
635 case GDK_KEY_Return:
636 nKeyCode = com::sun::star::awt::Key::RETURN;
637 break;
638 case GDK_KEY_Escape:
639 nKeyCode = com::sun::star::awt::Key::ESCAPE;
640 break;
641 case GDK_KEY_Tab:
642 nKeyCode = com::sun::star::awt::Key::TAB;
643 break;
644 case GDK_KEY_Down:
645 nKeyCode = com::sun::star::awt::Key::DOWN;
646 break;
647 case GDK_KEY_Up:
648 nKeyCode = com::sun::star::awt::Key::UP;
649 break;
650 case GDK_KEY_Left:
651 nKeyCode = com::sun::star::awt::Key::LEFT;
652 break;
653 case GDK_KEY_Right:
654 nKeyCode = com::sun::star::awt::Key::RIGHT;
655 break;
656 case GDK_KEY_Page_Down:
657 nKeyCode = com::sun::star::awt::Key::PAGEDOWN;
658 break;
659 case GDK_KEY_Page_Up:
660 nKeyCode = com::sun::star::awt::Key::PAGEUP;
661 break;
662 case GDK_KEY_Shift_L:
663 case GDK_KEY_Shift_R:
664 if (pEvent->type == GDK_KEY_PRESS)
665 priv->m_nKeyModifier |= KEY_SHIFT;
666 break;
667 case GDK_KEY_Control_L:
668 case GDK_KEY_Control_R:
669 if (pEvent->type == GDK_KEY_PRESS)
670 priv->m_nKeyModifier |= KEY_MOD1;
671 break;
672 case GDK_KEY_Alt_L:
673 case GDK_KEY_Alt_R:
674 if (pEvent->type == GDK_KEY_PRESS)
675 priv->m_nKeyModifier |= KEY_MOD2;
676 else
677 priv->m_nKeyModifier &= ~KEY_MOD2;
678 break;
679 default:
680 if (pEvent->keyval >= GDK_KEY_F1 && pEvent->keyval <= GDK_KEY_F26)
681 nKeyCode = com::sun::star::awt::Key::F1 + (pEvent->keyval - GDK_KEY_F1);
682 else
683 nCharCode = gdk_keyval_to_unicode(pEvent->keyval);
686 // rsc is not public API, but should be good enough for debugging purposes.
687 // If this is needed for real, then probably a new param of type
688 // css::awt::KeyModifier is needed in postKeyEvent().
689 if (pEvent->state & GDK_SHIFT_MASK)
690 nKeyCode |= KEY_SHIFT;
692 if (pEvent->state & GDK_CONTROL_MASK)
693 nKeyCode |= KEY_MOD1;
695 if (priv->m_nKeyModifier & KEY_MOD2)
696 nKeyCode |= KEY_MOD2;
698 if (nKeyCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2)) {
699 if (pEvent->keyval >= GDK_KEY_a && pEvent->keyval <= GDK_KEY_z)
701 nKeyCode |= 512 + (pEvent->keyval - GDK_KEY_a);
703 else if (pEvent->keyval >= GDK_KEY_A && pEvent->keyval <= GDK_KEY_Z) {
704 nKeyCode |= 512 + (pEvent->keyval - GDK_KEY_A);
706 else if (pEvent->keyval >= GDK_KEY_0 && pEvent->keyval <= GDK_KEY_9) {
707 nKeyCode |= 256 + (pEvent->keyval - GDK_KEY_0);
711 if (pEvent->type == GDK_KEY_RELEASE)
713 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
714 LOEvent* pLOEvent = new LOEvent(LOK_POST_KEY);
715 pLOEvent->m_nKeyEvent = LOK_KEYEVENT_KEYUP;
716 pLOEvent->m_nCharCode = nCharCode;
717 pLOEvent->m_nKeyCode = nKeyCode;
718 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
719 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
720 if (error != nullptr)
722 g_warning("Unable to call LOK_POST_KEY: %s", error->message);
723 g_clear_error(&error);
725 g_object_unref(task);
727 else
729 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
730 LOEvent* pLOEvent = new LOEvent(LOK_POST_KEY);
731 pLOEvent->m_nKeyEvent = LOK_KEYEVENT_KEYINPUT;
732 pLOEvent->m_nCharCode = nCharCode;
733 pLOEvent->m_nKeyCode = nKeyCode;
734 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
735 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
736 if (error != nullptr)
738 g_warning("Unable to call LOK_POST_KEY: %s", error->message);
739 g_clear_error(&error);
741 g_object_unref(task);
744 return FALSE;
747 static gboolean
748 handleTimeout (gpointer pData)
750 LOKDocView* pDocView = LOK_DOC_VIEW (pData);
751 LOKDocViewPrivate& priv = getPrivate(pDocView);
753 if (priv->m_bEdit)
755 if (priv->m_bCursorOverlayVisible)
756 priv->m_bCursorOverlayVisible = false;
757 else
758 priv->m_bCursorOverlayVisible = true;
759 gtk_widget_queue_draw(GTK_WIDGET(pDocView));
762 return G_SOURCE_CONTINUE;
765 static void
766 commandChanged(LOKDocView* pDocView, const std::string& rString)
768 g_signal_emit(pDocView, doc_view_signals[COMMAND_CHANGED], 0, rString.c_str());
771 static void
772 searchNotFound(LOKDocView* pDocView, const std::string& rString)
774 g_signal_emit(pDocView, doc_view_signals[SEARCH_NOT_FOUND], 0, rString.c_str());
777 static void searchResultCount(LOKDocView* pDocView, const std::string& rString)
779 g_signal_emit(pDocView, doc_view_signals[SEARCH_RESULT_COUNT], 0, rString.c_str());
782 static void commandResult(LOKDocView* pDocView, const std::string& rString)
784 g_signal_emit(pDocView, doc_view_signals[COMMAND_RESULT], 0, rString.c_str());
787 static void formulaChanged(LOKDocView* pDocView, const std::string& rString)
789 g_signal_emit(pDocView, doc_view_signals[FORMULA_CHANGED], 0, rString.c_str());
792 static void reportError(LOKDocView* /*pDocView*/, const std::string& rString)
794 GtkWidget *dialog = gtk_message_dialog_new(nullptr,
795 GTK_DIALOG_DESTROY_WITH_PARENT,
796 GTK_MESSAGE_ERROR,
797 GTK_BUTTONS_CLOSE,
798 "%s",
799 rString.c_str());
800 gtk_dialog_run(GTK_DIALOG(dialog));
801 gtk_widget_destroy(dialog);
804 static void
805 setPart(LOKDocView* pDocView, const std::string& rString)
807 g_signal_emit(pDocView, doc_view_signals[PART_CHANGED], 0, std::stoi(rString));
810 static void
811 hyperlinkClicked(LOKDocView* pDocView, const std::string& rString)
813 g_signal_emit(pDocView, doc_view_signals[HYPERLINK_CLICKED], 0, rString.c_str());
816 /// Trigger a redraw, invoked on the main thread by other functions running in a thread.
817 static gboolean queueDraw(gpointer pData)
819 GtkWidget* pWidget = static_cast<GtkWidget*>(pData);
821 gtk_widget_queue_draw(pWidget);
823 return G_SOURCE_REMOVE;
826 /// Set up LOKDocView after the document is loaded, invoked on the main thread by openDocumentInThread() running in a thread.
827 static gboolean postDocumentLoad(gpointer pData)
829 LOKDocView* pLOKDocView = static_cast<LOKDocView*>(pData);
830 LOKDocViewPrivate& priv = getPrivate(pLOKDocView);
832 priv->m_pDocument->pClass->setView(priv->m_pDocument, priv->m_nViewId);
833 priv->m_pDocument->pClass->initializeForRendering(priv->m_pDocument, priv->m_aRenderingArguments.c_str());
834 priv->m_pDocument->pClass->registerCallback(priv->m_pDocument, callbackWorker, pLOKDocView);
835 priv->m_pDocument->pClass->getDocumentSize(priv->m_pDocument, &priv->m_nDocumentWidthTwips, &priv->m_nDocumentHeightTwips);
836 priv->m_nParts = priv->m_pDocument->pClass->getParts(priv->m_pDocument);
837 g_timeout_add(600, handleTimeout, pLOKDocView);
839 float zoom = priv->m_fZoom;
840 long nDocumentWidthTwips = priv->m_nDocumentWidthTwips;
841 long nDocumentHeightTwips = priv->m_nDocumentHeightTwips;
842 long nDocumentWidthPixels = twipToPixel(nDocumentWidthTwips, zoom);
843 long nDocumentHeightPixels = twipToPixel(nDocumentHeightTwips, zoom);
844 // Total number of columns in this document.
845 guint nColumns = ceil((double)nDocumentWidthPixels / nTileSizePixels);
847 priv->m_pTileBuffer = std::unique_ptr<TileBuffer>(new TileBuffer(priv->m_pDocument,
848 nColumns));
849 gtk_widget_set_size_request(GTK_WIDGET(pLOKDocView),
850 nDocumentWidthPixels,
851 nDocumentHeightPixels);
852 gtk_widget_set_can_focus(GTK_WIDGET(pLOKDocView), TRUE);
853 gtk_widget_grab_focus(GTK_WIDGET(pLOKDocView));
854 lok_doc_view_set_zoom(pLOKDocView, 1.0);
856 return G_SOURCE_REMOVE;
859 /// Implementation of the global callback handler, invoked by globalCallback();
860 static gboolean
861 globalCallback (gpointer pData)
863 CallbackData* pCallback = static_cast<CallbackData*>(pData);
864 LOKDocViewPrivate& priv = getPrivate(pCallback->m_pDocView);
865 gboolean bModify = false;
867 switch (pCallback->m_nType)
869 case LOK_CALLBACK_STATUS_INDICATOR_START:
871 priv->m_nLoadProgress = 0.0;
872 g_signal_emit (pCallback->m_pDocView, doc_view_signals[LOAD_CHANGED], 0, 0.0);
874 break;
875 case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE:
877 priv->m_nLoadProgress = static_cast<gdouble>(std::stoi(pCallback->m_aPayload)/100.0);
878 g_signal_emit (pCallback->m_pDocView, doc_view_signals[LOAD_CHANGED], 0, priv->m_nLoadProgress);
880 break;
881 case LOK_CALLBACK_STATUS_INDICATOR_FINISH:
883 priv->m_nLoadProgress = 1.0;
884 g_signal_emit (pCallback->m_pDocView, doc_view_signals[LOAD_CHANGED], 0, 1.0);
886 break;
887 case LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY:
888 bModify = true;
889 SAL_FALLTHROUGH;
890 case LOK_CALLBACK_DOCUMENT_PASSWORD:
892 char const*const pURL(pCallback->m_aPayload.c_str());
893 g_signal_emit (pCallback->m_pDocView, doc_view_signals[PASSWORD_REQUIRED], 0, pURL, bModify);
895 break;
896 default:
897 g_assert(false);
898 break;
900 delete pCallback;
902 return G_SOURCE_REMOVE;
905 static void
906 globalCallbackWorker(int nType, const char* pPayload, void* pData)
908 LOKDocView* pDocView = LOK_DOC_VIEW (pData);
910 CallbackData* pCallback = new CallbackData(nType, pPayload ? pPayload : "(nil)", pDocView);
911 g_info("LOKDocView_Impl::globalCallbackWorkerImpl: %s, '%s'", callbackTypeToString(nType), pPayload);
912 gdk_threads_add_idle(globalCallback, pCallback);
915 static GdkRectangle
916 payloadToRectangle (LOKDocView* pDocView, const char* pPayload)
918 LOKDocViewPrivate& priv = getPrivate(pDocView);
919 GdkRectangle aRet;
920 gchar** ppCoordinates = g_strsplit(pPayload, ", ", 4);
921 gchar** ppCoordinate = ppCoordinates;
923 aRet.width = aRet.height = aRet.x = aRet.y = 0;
925 if (!*ppCoordinate)
926 return aRet;
927 aRet.x = atoi(*ppCoordinate);
928 if (aRet.x < 0)
929 aRet.x = 0;
930 ++ppCoordinate;
931 if (!*ppCoordinate)
932 return aRet;
933 aRet.y = atoi(*ppCoordinate);
934 if (aRet.y < 0)
935 aRet.y = 0;
936 ++ppCoordinate;
937 if (!*ppCoordinate)
938 return aRet;
939 aRet.width = atoi(*ppCoordinate);
940 if (aRet.x + aRet.width > priv->m_nDocumentWidthTwips)
941 aRet.width = priv->m_nDocumentWidthTwips - aRet.x;
942 ++ppCoordinate;
943 if (!*ppCoordinate)
944 return aRet;
945 aRet.height = atoi(*ppCoordinate);
946 if (aRet.y + aRet.height > priv->m_nDocumentHeightTwips)
947 aRet.height = priv->m_nDocumentHeightTwips - aRet.y;
948 g_strfreev(ppCoordinates);
950 return aRet;
953 static const std::vector<GdkRectangle>
954 payloadToRectangles(LOKDocView* pDocView, const char* pPayload)
956 std::vector<GdkRectangle> aRet;
958 gchar** ppRectangles = g_strsplit(pPayload, "; ", 0);
959 for (gchar** ppRectangle = ppRectangles; *ppRectangle; ++ppRectangle)
960 aRet.push_back(payloadToRectangle(pDocView, *ppRectangle));
961 g_strfreev(ppRectangles);
963 return aRet;
967 static void
968 setTilesInvalid (LOKDocView* pDocView, const GdkRectangle& rRectangle)
970 LOKDocViewPrivate& priv = getPrivate(pDocView);
971 GdkRectangle aRectanglePixels;
972 GdkPoint aStart, aEnd;
974 aRectanglePixels.x = twipToPixel(rRectangle.x, priv->m_fZoom);
975 aRectanglePixels.y = twipToPixel(rRectangle.y, priv->m_fZoom);
976 aRectanglePixels.width = twipToPixel(rRectangle.width, priv->m_fZoom);
977 aRectanglePixels.height = twipToPixel(rRectangle.height, priv->m_fZoom);
979 aStart.x = aRectanglePixels.y / nTileSizePixels;
980 aStart.y = aRectanglePixels.x / nTileSizePixels;
981 aEnd.x = (aRectanglePixels.y + aRectanglePixels.height + nTileSizePixels) / nTileSizePixels;
982 aEnd.y = (aRectanglePixels.x + aRectanglePixels.width + nTileSizePixels) / nTileSizePixels;
983 for (int i = aStart.x; i < aEnd.x; i++)
985 for (int j = aStart.y; j < aEnd.y; j++)
987 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
988 priv->m_pTileBuffer->setInvalid(i, j, priv->m_fZoom, task, priv->lokThreadPool);
989 g_object_unref(task);
994 static gboolean
995 callback (gpointer pData)
997 CallbackData* pCallback = static_cast<CallbackData*>(pData);
998 LOKDocView* pDocView = LOK_DOC_VIEW (pCallback->m_pDocView);
999 LOKDocViewPrivate& priv = getPrivate(pDocView);
1001 switch (pCallback->m_nType)
1003 case LOK_CALLBACK_INVALIDATE_TILES:
1005 if (pCallback->m_aPayload != "EMPTY")
1007 GdkRectangle aRectangle = payloadToRectangle(pDocView, pCallback->m_aPayload.c_str());
1008 setTilesInvalid(pDocView, aRectangle);
1010 else
1011 priv->m_pTileBuffer->resetAllTiles();
1013 gtk_widget_queue_draw(GTK_WIDGET(pDocView));
1015 break;
1016 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
1018 priv->m_aVisibleCursor = payloadToRectangle(pDocView, pCallback->m_aPayload.c_str());
1019 priv->m_bCursorOverlayVisible = true;
1020 g_signal_emit(pDocView, doc_view_signals[CURSOR_CHANGED], 0,
1021 priv->m_aVisibleCursor.x,
1022 priv->m_aVisibleCursor.y,
1023 priv->m_aVisibleCursor.width,
1024 priv->m_aVisibleCursor.height);
1025 gtk_widget_queue_draw(GTK_WIDGET(pDocView));
1027 break;
1028 case LOK_CALLBACK_TEXT_SELECTION:
1030 priv->m_aTextSelectionRectangles = payloadToRectangles(pDocView, pCallback->m_aPayload.c_str());
1031 gboolean bIsTextSelected = !priv->m_aTextSelectionRectangles.empty();
1032 // In case the selection is empty, then we get no LOK_CALLBACK_TEXT_SELECTION_START/END events.
1033 if (!bIsTextSelected)
1035 memset(&priv->m_aTextSelectionStart, 0, sizeof(priv->m_aTextSelectionStart));
1036 memset(&priv->m_aHandleStartRect, 0, sizeof(priv->m_aHandleStartRect));
1037 memset(&priv->m_aTextSelectionEnd, 0, sizeof(priv->m_aTextSelectionEnd));
1038 memset(&priv->m_aHandleEndRect, 0, sizeof(priv->m_aHandleEndRect));
1040 else
1041 memset(&priv->m_aHandleMiddleRect, 0, sizeof(priv->m_aHandleMiddleRect));
1043 g_signal_emit(pDocView, doc_view_signals[TEXT_SELECTION], 0, bIsTextSelected);
1044 gtk_widget_queue_draw(GTK_WIDGET(pDocView));
1046 break;
1047 case LOK_CALLBACK_TEXT_SELECTION_START:
1049 priv->m_aTextSelectionStart = payloadToRectangle(pDocView, pCallback->m_aPayload.c_str());
1051 break;
1052 case LOK_CALLBACK_TEXT_SELECTION_END:
1054 priv->m_aTextSelectionEnd = payloadToRectangle(pDocView, pCallback->m_aPayload.c_str());
1056 break;
1057 case LOK_CALLBACK_CURSOR_VISIBLE:
1059 priv->m_bCursorVisible = pCallback->m_aPayload == "true";
1061 break;
1062 case LOK_CALLBACK_MOUSE_POINTER:
1064 // We do not want the cursor to get changed in view-only mode
1065 if (priv->m_bEdit)
1067 // The gtk docs claim that most css cursors should be supported, however
1068 // on my system at least this is not true and many cursors are unsupported.
1069 // In this case pCursor = null, which results in the default cursor
1070 // being set.
1071 GdkCursor* pCursor = gdk_cursor_new_from_name(gtk_widget_get_display(GTK_WIDGET(pDocView)),
1072 pCallback->m_aPayload.c_str());
1073 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(pDocView)), pCursor);
1076 break;
1077 case LOK_CALLBACK_GRAPHIC_SELECTION:
1079 if (pCallback->m_aPayload != "EMPTY")
1080 priv->m_aGraphicSelection = payloadToRectangle(pDocView, pCallback->m_aPayload.c_str());
1081 else
1082 memset(&priv->m_aGraphicSelection, 0, sizeof(priv->m_aGraphicSelection));
1083 gtk_widget_queue_draw(GTK_WIDGET(pDocView));
1085 break;
1086 case LOK_CALLBACK_CELL_CURSOR:
1088 if (pCallback->m_aPayload != "EMPTY")
1089 priv->m_aCellCursor = payloadToRectangle(pDocView, pCallback->m_aPayload.c_str());
1090 else
1091 memset(&priv->m_aCellCursor, 0, sizeof(priv->m_aCellCursor));
1092 gtk_widget_queue_draw(GTK_WIDGET(pDocView));
1094 break;
1095 case LOK_CALLBACK_HYPERLINK_CLICKED:
1097 hyperlinkClicked(pDocView, pCallback->m_aPayload);
1099 break;
1100 case LOK_CALLBACK_STATE_CHANGED:
1102 commandChanged(pDocView, pCallback->m_aPayload);
1104 break;
1105 case LOK_CALLBACK_SEARCH_NOT_FOUND:
1107 searchNotFound(pDocView, pCallback->m_aPayload);
1109 break;
1110 case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED:
1112 payloadToSize(pCallback->m_aPayload.c_str(), priv->m_nDocumentWidthTwips, priv->m_nDocumentHeightTwips);
1113 gtk_widget_set_size_request(GTK_WIDGET(pDocView),
1114 twipToPixel(priv->m_nDocumentWidthTwips, priv->m_fZoom),
1115 twipToPixel(priv->m_nDocumentHeightTwips, priv->m_fZoom));
1117 g_signal_emit(pDocView, doc_view_signals[SIZE_CHANGED], 0, nullptr);
1119 break;
1120 case LOK_CALLBACK_SET_PART:
1122 setPart(pDocView, pCallback->m_aPayload);
1124 break;
1125 case LOK_CALLBACK_SEARCH_RESULT_SELECTION:
1127 boost::property_tree::ptree aTree;
1128 std::stringstream aStream(pCallback->m_aPayload);
1129 boost::property_tree::read_json(aStream, aTree);
1130 int nCount = aTree.get_child("searchResultSelection").size();
1131 searchResultCount(pDocView, std::to_string(nCount));
1133 break;
1134 case LOK_CALLBACK_UNO_COMMAND_RESULT:
1136 commandResult(pDocView, pCallback->m_aPayload);
1138 break;
1139 case LOK_CALLBACK_CELL_FORMULA:
1141 formulaChanged(pDocView, pCallback->m_aPayload);
1143 break;
1144 case LOK_CALLBACK_ERROR:
1146 reportError(pDocView, pCallback->m_aPayload);
1148 break;
1149 case LOK_CALLBACK_CONTEXT_MENU:
1151 // TODO: Implement me
1152 break;
1154 default:
1155 g_assert(false);
1156 break;
1158 delete pCallback;
1160 return G_SOURCE_REMOVE;
1163 static void callbackWorker (int nType, const char* pPayload, void* pData)
1165 LOKDocView* pDocView = LOK_DOC_VIEW (pData);
1167 CallbackData* pCallback = new CallbackData(nType, pPayload ? pPayload : "(nil)", pDocView);
1168 g_info("callbackWorker: %s, '%s'", callbackTypeToString(nType), pPayload);
1169 gdk_threads_add_idle(callback, pCallback);
1172 static void
1173 renderHandle(LOKDocView* pDocView,
1174 cairo_t* pCairo,
1175 const GdkRectangle& rCursor,
1176 cairo_surface_t* pHandle,
1177 GdkRectangle& rRectangle)
1179 LOKDocViewPrivate& priv = getPrivate(pDocView);
1180 GdkPoint aCursorBottom;
1181 int nHandleWidth, nHandleHeight;
1182 double fHandleScale;
1184 nHandleWidth = cairo_image_surface_get_width(pHandle);
1185 nHandleHeight = cairo_image_surface_get_height(pHandle);
1186 // We want to scale down the handle, so that its height is the same as the cursor caret.
1187 fHandleScale = twipToPixel(rCursor.height, priv->m_fZoom) / nHandleHeight;
1188 // We want the top center of the handle bitmap to be at the bottom center of the cursor rectangle.
1189 aCursorBottom.x = twipToPixel(rCursor.x, priv->m_fZoom) + twipToPixel(rCursor.width, priv->m_fZoom) / 2 - (nHandleWidth * fHandleScale) / 2;
1190 aCursorBottom.y = twipToPixel(rCursor.y, priv->m_fZoom) + twipToPixel(rCursor.height, priv->m_fZoom);
1192 cairo_save (pCairo);
1193 cairo_translate(pCairo, aCursorBottom.x, aCursorBottom.y);
1194 cairo_scale(pCairo, fHandleScale, fHandleScale);
1195 cairo_set_source_surface(pCairo, pHandle, 0, 0);
1196 cairo_paint(pCairo);
1197 cairo_restore (pCairo);
1199 rRectangle.x = aCursorBottom.x;
1200 rRectangle.y = aCursorBottom.y;
1201 rRectangle.width = nHandleWidth * fHandleScale;
1202 rRectangle.height = nHandleHeight * fHandleScale;
1205 /// Renders pHandle around an rSelection rectangle on pCairo.
1206 static void
1207 renderGraphicHandle(LOKDocView* pDocView,
1208 cairo_t* pCairo,
1209 const GdkRectangle& rSelection,
1210 cairo_surface_t* pHandle)
1212 LOKDocViewPrivate& priv = getPrivate(pDocView);
1213 int nHandleWidth, nHandleHeight;
1214 GdkRectangle aSelection;
1216 nHandleWidth = cairo_image_surface_get_width(pHandle);
1217 nHandleHeight = cairo_image_surface_get_height(pHandle);
1219 aSelection.x = twipToPixel(rSelection.x, priv->m_fZoom);
1220 aSelection.y = twipToPixel(rSelection.y, priv->m_fZoom);
1221 aSelection.width = twipToPixel(rSelection.width, priv->m_fZoom);
1222 aSelection.height = twipToPixel(rSelection.height, priv->m_fZoom);
1224 for (int i = 0; i < GRAPHIC_HANDLE_COUNT; ++i)
1226 int x = aSelection.x, y = aSelection.y;
1228 switch (i)
1230 case 0: // top-left
1231 break;
1232 case 1: // top-middle
1233 x += aSelection.width / 2;
1234 break;
1235 case 2: // top-right
1236 x += aSelection.width;
1237 break;
1238 case 3: // middle-left
1239 y += aSelection.height / 2;
1240 break;
1241 case 4: // middle-right
1242 x += aSelection.width;
1243 y += aSelection.height / 2;
1244 break;
1245 case 5: // bottom-left
1246 y += aSelection.height;
1247 break;
1248 case 6: // bottom-middle
1249 x += aSelection.width / 2;
1250 y += aSelection.height;
1251 break;
1252 case 7: // bottom-right
1253 x += aSelection.width;
1254 y += aSelection.height;
1255 break;
1258 // Center the handle.
1259 x -= nHandleWidth / 2;
1260 y -= nHandleHeight / 2;
1262 priv->m_aGraphicHandleRects[i].x = x;
1263 priv->m_aGraphicHandleRects[i].y = y;
1264 priv->m_aGraphicHandleRects[i].width = nHandleWidth;
1265 priv->m_aGraphicHandleRects[i].height = nHandleHeight;
1267 cairo_save (pCairo);
1268 cairo_translate(pCairo, x, y);
1269 cairo_set_source_surface(pCairo, pHandle, 0, 0);
1270 cairo_paint(pCairo);
1271 cairo_restore (pCairo);
1275 /// Finishes the paint tile operation and returns the result, if any
1276 static gpointer
1277 paintTileFinish(LOKDocView* pDocView, GAsyncResult* res, GError **error)
1279 GTask* task = G_TASK(res);
1281 g_return_val_if_fail(LOK_IS_DOC_VIEW(pDocView), nullptr);
1282 g_return_val_if_fail(g_task_is_valid(res, pDocView), nullptr);
1283 g_return_val_if_fail(error == nullptr || *error == nullptr, nullptr);
1285 return g_task_propagate_pointer(task, error);
1288 /// Callback called in the main UI thread when paintTileInThread in LOK thread has finished
1289 static void
1290 paintTileCallback(GObject* sourceObject, GAsyncResult* res, gpointer userData)
1292 LOKDocView* pDocView = LOK_DOC_VIEW(sourceObject);
1293 LOKDocViewPrivate& priv = getPrivate(pDocView);
1294 LOEvent* pLOEvent = static_cast<LOEvent*>(userData);
1295 std::unique_ptr<TileBuffer>& buffer = priv->m_pTileBuffer;
1296 int index = pLOEvent->m_nPaintTileX * buffer->m_nWidth + pLOEvent->m_nPaintTileY;
1297 GError* error;
1299 error = nullptr;
1300 cairo_surface_t* pSurface = static_cast<cairo_surface_t*>(paintTileFinish(pDocView, res, &error));
1301 if (error != nullptr)
1303 if (error->domain == LOK_TILEBUFFER_ERROR &&
1304 error->code == LOK_TILEBUFFER_CHANGED)
1305 g_info("Skipping paint tile request because corresponding"
1306 "tile buffer has been destroyed");
1307 else
1308 g_warning("Unable to get painted GdkPixbuf: %s", error->message);
1309 g_error_free(error);
1310 return;
1313 buffer->m_mTiles[index].setSurface(pSurface);
1314 buffer->m_mTiles[index].valid = true;
1315 gdk_threads_add_idle(queueDraw, GTK_WIDGET(pDocView));
1317 cairo_surface_destroy(pSurface);
1321 static gboolean
1322 renderDocument(LOKDocView* pDocView, cairo_t* pCairo)
1324 LOKDocViewPrivate& priv = getPrivate(pDocView);
1325 GdkRectangle aVisibleArea;
1326 long nDocumentWidthPixels = twipToPixel(priv->m_nDocumentWidthTwips, priv->m_fZoom);
1327 long nDocumentHeightPixels = twipToPixel(priv->m_nDocumentHeightTwips, priv->m_fZoom);
1328 // Total number of rows / columns in this document.
1329 guint nRows = ceil((double)nDocumentHeightPixels / nTileSizePixels);
1330 guint nColumns = ceil((double)nDocumentWidthPixels / nTileSizePixels);
1332 gdk_cairo_get_clip_rectangle (pCairo, &aVisibleArea);
1333 aVisibleArea.x = pixelToTwip (aVisibleArea.x, priv->m_fZoom);
1334 aVisibleArea.y = pixelToTwip (aVisibleArea.y, priv->m_fZoom);
1335 aVisibleArea.width = pixelToTwip (aVisibleArea.width, priv->m_fZoom);
1336 aVisibleArea.height = pixelToTwip (aVisibleArea.height, priv->m_fZoom);
1338 // Render the tiles.
1339 for (guint nRow = 0; nRow < nRows; ++nRow)
1341 for (guint nColumn = 0; nColumn < nColumns; ++nColumn)
1343 GdkRectangle aTileRectangleTwips, aTileRectanglePixels;
1344 bool bPaint = true;
1346 // Determine size of the tile: the rightmost/bottommost tiles may
1347 // be smaller, and we need the size to decide if we need to repaint.
1348 if (nColumn == nColumns - 1)
1349 aTileRectanglePixels.width = nDocumentWidthPixels - nColumn * nTileSizePixels;
1350 else
1351 aTileRectanglePixels.width = nTileSizePixels;
1352 if (nRow == nRows - 1)
1353 aTileRectanglePixels.height = nDocumentHeightPixels - nRow * nTileSizePixels;
1354 else
1355 aTileRectanglePixels.height = nTileSizePixels;
1357 // Determine size and position of the tile in document coordinates,
1358 // so we can decide if we can skip painting for partial rendering.
1359 aTileRectangleTwips.x = pixelToTwip(nTileSizePixels, priv->m_fZoom) * nColumn;
1360 aTileRectangleTwips.y = pixelToTwip(nTileSizePixels, priv->m_fZoom) * nRow;
1361 aTileRectangleTwips.width = pixelToTwip(aTileRectanglePixels.width, priv->m_fZoom);
1362 aTileRectangleTwips.height = pixelToTwip(aTileRectanglePixels.height, priv->m_fZoom);
1364 if (!gdk_rectangle_intersect(&aVisibleArea, &aTileRectangleTwips, nullptr))
1365 bPaint = false;
1367 if (bPaint)
1369 LOEvent* pLOEvent = new LOEvent(LOK_PAINT_TILE);
1370 pLOEvent->m_nPaintTileX = nRow;
1371 pLOEvent->m_nPaintTileY = nColumn;
1372 pLOEvent->m_fPaintTileZoom = priv->m_fZoom;
1373 pLOEvent->m_pTileBuffer = &*priv->m_pTileBuffer;
1374 GTask* task = g_task_new(pDocView, nullptr, paintTileCallback, pLOEvent);
1375 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
1377 Tile& currentTile = priv->m_pTileBuffer->getTile(nRow, nColumn, task, priv->lokThreadPool);
1378 cairo_surface_t* pSurface = currentTile.getBuffer();
1379 cairo_set_source_surface(pCairo, pSurface,
1380 twipToPixel(aTileRectangleTwips.x, priv->m_fZoom),
1381 twipToPixel(aTileRectangleTwips.y, priv->m_fZoom));
1382 cairo_paint(pCairo);
1383 g_object_unref(task);
1388 return FALSE;
1391 static gboolean
1392 renderOverlay(LOKDocView* pDocView, cairo_t* pCairo)
1394 LOKDocViewPrivate& priv = getPrivate(pDocView);
1396 if (priv->m_bEdit && priv->m_bCursorVisible && priv->m_bCursorOverlayVisible && !isEmptyRectangle(priv->m_aVisibleCursor))
1398 if (priv->m_aVisibleCursor.width < 30)
1399 // Set a minimal width if it would be 0.
1400 priv->m_aVisibleCursor.width = 30;
1402 cairo_set_source_rgb(pCairo, 0, 0, 0);
1403 cairo_rectangle(pCairo,
1404 twipToPixel(priv->m_aVisibleCursor.x, priv->m_fZoom),
1405 twipToPixel(priv->m_aVisibleCursor.y, priv->m_fZoom),
1406 twipToPixel(priv->m_aVisibleCursor.width, priv->m_fZoom),
1407 twipToPixel(priv->m_aVisibleCursor.height, priv->m_fZoom));
1408 cairo_fill(pCairo);
1411 if (priv->m_bEdit && priv->m_bCursorVisible && !isEmptyRectangle(priv->m_aVisibleCursor) && priv->m_aTextSelectionRectangles.empty())
1413 // Have a cursor, but no selection: we need the middle handle.
1414 gchar* handleMiddlePath = g_strconcat (priv->m_aLOPath, CURSOR_HANDLE_DIR, "handle_image_middle.png", nullptr);
1415 if (!priv->m_pHandleMiddle)
1417 priv->m_pHandleMiddle = cairo_image_surface_create_from_png(handleMiddlePath);
1418 assert(cairo_surface_status(priv->m_pHandleMiddle) == CAIRO_STATUS_SUCCESS);
1420 g_free (handleMiddlePath);
1421 renderHandle(pDocView, pCairo, priv->m_aVisibleCursor, priv->m_pHandleMiddle, priv->m_aHandleMiddleRect);
1424 if (!priv->m_aTextSelectionRectangles.empty())
1426 for (GdkRectangle& rRectangle : priv->m_aTextSelectionRectangles)
1428 // Blue with 75% transparency.
1429 cairo_set_source_rgba(pCairo, ((double)0x43)/255, ((double)0xac)/255, ((double)0xe8)/255, 0.25);
1430 cairo_rectangle(pCairo,
1431 twipToPixel(rRectangle.x, priv->m_fZoom),
1432 twipToPixel(rRectangle.y, priv->m_fZoom),
1433 twipToPixel(rRectangle.width, priv->m_fZoom),
1434 twipToPixel(rRectangle.height, priv->m_fZoom));
1435 cairo_fill(pCairo);
1438 // Handles
1439 if (!isEmptyRectangle(priv->m_aTextSelectionStart))
1441 // Have a start position: we need a start handle.
1442 gchar* handleStartPath = g_strconcat (priv->m_aLOPath, CURSOR_HANDLE_DIR, "handle_image_start.png", nullptr);
1443 if (!priv->m_pHandleStart)
1445 priv->m_pHandleStart = cairo_image_surface_create_from_png(handleStartPath);
1446 assert(cairo_surface_status(priv->m_pHandleStart) == CAIRO_STATUS_SUCCESS);
1448 renderHandle(pDocView, pCairo, priv->m_aTextSelectionStart, priv->m_pHandleStart, priv->m_aHandleStartRect);
1449 g_free (handleStartPath);
1451 if (!isEmptyRectangle(priv->m_aTextSelectionEnd))
1453 // Have a start position: we need an end handle.
1454 gchar* handleEndPath = g_strconcat (priv->m_aLOPath, CURSOR_HANDLE_DIR, "handle_image_end.png", nullptr);
1455 if (!priv->m_pHandleEnd)
1457 priv->m_pHandleEnd = cairo_image_surface_create_from_png(handleEndPath);
1458 assert(cairo_surface_status(priv->m_pHandleEnd) == CAIRO_STATUS_SUCCESS);
1460 renderHandle(pDocView, pCairo, priv->m_aTextSelectionEnd, priv->m_pHandleEnd, priv->m_aHandleEndRect);
1461 g_free (handleEndPath);
1465 if (!isEmptyRectangle(priv->m_aGraphicSelection))
1467 gchar* handleGraphicPath = g_strconcat (priv->m_aLOPath, CURSOR_HANDLE_DIR, "handle_graphic.png", nullptr);
1468 if (!priv->m_pGraphicHandle)
1470 priv->m_pGraphicHandle = cairo_image_surface_create_from_png(handleGraphicPath);
1471 assert(cairo_surface_status(priv->m_pGraphicHandle) == CAIRO_STATUS_SUCCESS);
1473 renderGraphicHandle(pDocView, pCairo, priv->m_aGraphicSelection, priv->m_pGraphicHandle);
1474 g_free (handleGraphicPath);
1477 if (!isEmptyRectangle(priv->m_aCellCursor))
1479 cairo_set_source_rgb(pCairo, 0, 0, 0);
1480 cairo_rectangle(pCairo,
1481 twipToPixel(priv->m_aCellCursor.x, priv->m_fZoom),
1482 twipToPixel(priv->m_aCellCursor.y, priv->m_fZoom),
1483 twipToPixel(priv->m_aCellCursor.width, priv->m_fZoom),
1484 twipToPixel(priv->m_aCellCursor.height, priv->m_fZoom));
1485 // priv->m_aCellCursor.x - 1,
1486 // priv->m_aCellCursor.y - 1,
1487 // priv->m_aCellCursor.width + 2,
1488 // priv->m_aCellCursor.height + 2);
1489 cairo_set_line_width(pCairo, 2.0);
1490 cairo_stroke(pCairo);
1493 return FALSE;
1496 static gboolean
1497 lok_doc_view_signal_button(GtkWidget* pWidget, GdkEventButton* pEvent)
1499 LOKDocView* pDocView = LOK_DOC_VIEW (pWidget);
1500 LOKDocViewPrivate& priv = getPrivate(pDocView);
1501 GError* error = nullptr;
1503 g_info("LOKDocView_Impl::signalButton: %d, %d (in twips: %d, %d)",
1504 (int)pEvent->x, (int)pEvent->y,
1505 (int)pixelToTwip(pEvent->x, priv->m_fZoom),
1506 (int)pixelToTwip(pEvent->y, priv->m_fZoom));
1507 gtk_widget_grab_focus(GTK_WIDGET(pDocView));
1509 switch (pEvent->type)
1511 case GDK_BUTTON_PRESS:
1513 GdkRectangle aClick;
1514 aClick.x = pEvent->x;
1515 aClick.y = pEvent->y;
1516 aClick.width = 1;
1517 aClick.height = 1;
1519 if (handleTextSelectionOnButtonPress(aClick, pDocView))
1520 return FALSE;
1521 if (handleGraphicSelectionOnButtonPress(aClick, pDocView))
1522 return FALSE;
1524 int nCount = 1;
1525 if ((pEvent->time - priv->m_nLastButtonPressTime) < 250)
1526 nCount++;
1527 priv->m_nLastButtonPressTime = pEvent->time;
1528 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
1529 LOEvent* pLOEvent = new LOEvent(LOK_POST_MOUSE_EVENT);
1530 pLOEvent->m_nPostMouseEventType = LOK_MOUSEEVENT_MOUSEBUTTONDOWN;
1531 pLOEvent->m_nPostMouseEventX = pixelToTwip(pEvent->x, priv->m_fZoom);
1532 pLOEvent->m_nPostMouseEventY = pixelToTwip(pEvent->y, priv->m_fZoom);
1533 pLOEvent->m_nPostMouseEventCount = nCount;
1534 switch (pEvent->button)
1536 case 1:
1537 pLOEvent->m_nPostMouseEventButton = MOUSE_LEFT;
1538 break;
1539 case 2:
1540 pLOEvent->m_nPostMouseEventButton = MOUSE_MIDDLE;
1541 break;
1542 case 3:
1543 pLOEvent->m_nPostMouseEventButton = MOUSE_RIGHT;
1544 break;
1546 pLOEvent->m_nPostMouseEventModifier = priv->m_nKeyModifier;
1547 priv->m_nLastButtonPressed = pLOEvent->m_nPostMouseEventButton;
1548 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
1550 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
1551 if (error != nullptr)
1553 g_warning("Unable to call LOK_POST_MOUSE_EVENT: %s", error->message);
1554 g_clear_error(&error);
1556 g_object_unref(task);
1557 break;
1559 case GDK_BUTTON_RELEASE:
1561 if (handleTextSelectionOnButtonRelease(pDocView))
1562 return FALSE;
1563 if (handleGraphicSelectionOnButtonRelease(pDocView, pEvent))
1564 return FALSE;
1566 int nCount = 1;
1567 if ((pEvent->time - priv->m_nLastButtonReleaseTime) < 250)
1568 nCount++;
1569 priv->m_nLastButtonReleaseTime = pEvent->time;
1570 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
1571 LOEvent* pLOEvent = new LOEvent(LOK_POST_MOUSE_EVENT);
1572 pLOEvent->m_nPostMouseEventType = LOK_MOUSEEVENT_MOUSEBUTTONUP;
1573 pLOEvent->m_nPostMouseEventX = pixelToTwip(pEvent->x, priv->m_fZoom);
1574 pLOEvent->m_nPostMouseEventY = pixelToTwip(pEvent->y, priv->m_fZoom);
1575 pLOEvent->m_nPostMouseEventCount = nCount;
1576 switch (pEvent->button)
1578 case 1:
1579 pLOEvent->m_nPostMouseEventButton = MOUSE_LEFT;
1580 break;
1581 case 2:
1582 pLOEvent->m_nPostMouseEventButton = MOUSE_MIDDLE;
1583 break;
1584 case 3:
1585 pLOEvent->m_nPostMouseEventButton = MOUSE_RIGHT;
1586 break;
1588 pLOEvent->m_nPostMouseEventModifier = priv->m_nKeyModifier;
1589 priv->m_nLastButtonPressed = pLOEvent->m_nPostMouseEventButton;
1590 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
1592 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
1593 if (error != nullptr)
1595 g_warning("Unable to call LOK_POST_MOUSE_EVENT: %s", error->message);
1596 g_clear_error(&error);
1598 g_object_unref(task);
1599 break;
1601 default:
1602 break;
1604 return FALSE;
1607 static void
1608 getDragPoint(GdkRectangle* pHandle,
1609 GdkEventMotion* pEvent,
1610 GdkPoint* pPoint)
1612 GdkPoint aCursor, aHandle;
1614 // Center of the cursor rectangle: we know that it's above the handle.
1615 aCursor.x = pHandle->x + pHandle->width / 2;
1616 aCursor.y = pHandle->y - pHandle->height / 2;
1617 // Center of the handle rectangle.
1618 aHandle.x = pHandle->x + pHandle->width / 2;
1619 aHandle.y = pHandle->y + pHandle->height / 2;
1620 // Our target is the original cursor position + the dragged offset.
1621 pPoint->x = aCursor.x + (pEvent->x - aHandle.x);
1622 pPoint->y = aCursor.y + (pEvent->y - aHandle.y);
1625 static gboolean
1626 lok_doc_view_signal_motion (GtkWidget* pWidget, GdkEventMotion* pEvent)
1628 LOKDocView* pDocView = LOK_DOC_VIEW (pWidget);
1629 LOKDocViewPrivate& priv = getPrivate(pDocView);
1630 GdkPoint aPoint;
1631 GError* error = nullptr;
1633 priv->m_pDocument->pClass->setView(priv->m_pDocument, priv->m_nViewId);
1634 if (priv->m_bInDragMiddleHandle)
1636 g_info("lcl_signalMotion: dragging the middle handle");
1637 getDragPoint(&priv->m_aHandleMiddleRect, pEvent, &aPoint);
1638 priv->m_pDocument->pClass->setTextSelection(priv->m_pDocument, LOK_SETTEXTSELECTION_RESET, pixelToTwip(aPoint.x, priv->m_fZoom), pixelToTwip(aPoint.y, priv->m_fZoom));
1639 return FALSE;
1641 if (priv->m_bInDragStartHandle)
1643 g_info("lcl_signalMotion: dragging the start handle");
1644 getDragPoint(&priv->m_aHandleStartRect, pEvent, &aPoint);
1645 priv->m_pDocument->pClass->setTextSelection(priv->m_pDocument, LOK_SETTEXTSELECTION_START, pixelToTwip(aPoint.x, priv->m_fZoom), pixelToTwip(aPoint.y, priv->m_fZoom));
1646 return FALSE;
1648 if (priv->m_bInDragEndHandle)
1650 g_info("lcl_signalMotion: dragging the end handle");
1651 getDragPoint(&priv->m_aHandleEndRect, pEvent, &aPoint);
1652 priv->m_pDocument->pClass->setTextSelection(priv->m_pDocument, LOK_SETTEXTSELECTION_END, pixelToTwip(aPoint.x, priv->m_fZoom), pixelToTwip(aPoint.y, priv->m_fZoom));
1653 return FALSE;
1655 for (int i = 0; i < GRAPHIC_HANDLE_COUNT; ++i)
1657 if (priv->m_bInDragGraphicHandles[i])
1659 g_info("lcl_signalMotion: dragging the graphic handle #%d", i);
1660 return FALSE;
1663 if (priv->m_bInDragGraphicSelection)
1665 g_info("lcl_signalMotion: dragging the graphic selection");
1666 return FALSE;
1669 GdkRectangle aMotionInTwipsInTwips;
1670 aMotionInTwipsInTwips.x = pixelToTwip(pEvent->x, priv->m_fZoom);
1671 aMotionInTwipsInTwips.y = pixelToTwip(pEvent->y, priv->m_fZoom);
1672 aMotionInTwipsInTwips.width = 1;
1673 aMotionInTwipsInTwips.height = 1;
1674 if (gdk_rectangle_intersect(&aMotionInTwipsInTwips, &priv->m_aGraphicSelection, nullptr))
1676 g_info("lcl_signalMotion: start of drag graphic selection");
1677 priv->m_bInDragGraphicSelection = true;
1679 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
1680 LOEvent* pLOEvent = new LOEvent(LOK_SET_GRAPHIC_SELECTION);
1681 pLOEvent->m_nSetGraphicSelectionType = LOK_SETGRAPHICSELECTION_START;
1682 pLOEvent->m_nSetGraphicSelectionX = pixelToTwip(pEvent->x, priv->m_fZoom);
1683 pLOEvent->m_nSetGraphicSelectionY = pixelToTwip(pEvent->y, priv->m_fZoom);
1684 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
1686 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
1687 if (error != nullptr)
1689 g_warning("Unable to call LOK_SET_GRAPHIC_SELECTION: %s", error->message);
1690 g_clear_error(&error);
1692 g_object_unref(task);
1694 return FALSE;
1697 // Otherwise a mouse move, as on the desktop.
1699 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
1700 LOEvent* pLOEvent = new LOEvent(LOK_POST_MOUSE_EVENT);
1701 pLOEvent->m_nPostMouseEventType = LOK_MOUSEEVENT_MOUSEMOVE;
1702 pLOEvent->m_nPostMouseEventX = pixelToTwip(pEvent->x, priv->m_fZoom);
1703 pLOEvent->m_nPostMouseEventY = pixelToTwip(pEvent->y, priv->m_fZoom);
1704 pLOEvent->m_nPostMouseEventCount = 1;
1705 pLOEvent->m_nPostMouseEventButton = priv->m_nLastButtonPressed;
1706 pLOEvent->m_nPostMouseEventModifier = priv->m_nKeyModifier;
1708 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
1710 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
1711 if (error != nullptr)
1713 g_warning("Unable to call LOK_MOUSEEVENT_MOUSEMOVE: %s", error->message);
1714 g_clear_error(&error);
1716 g_object_unref(task);
1718 return FALSE;
1721 static void
1722 setGraphicSelectionInThread(gpointer data)
1724 GTask* task = G_TASK(data);
1725 LOKDocView* pDocView = LOK_DOC_VIEW(g_task_get_source_object(task));
1726 LOKDocViewPrivate& priv = getPrivate(pDocView);
1727 LOEvent* pLOEvent = static_cast<LOEvent*>(g_task_get_task_data(task));
1729 priv->m_pDocument->pClass->setView(priv->m_pDocument, priv->m_nViewId);
1730 std::stringstream ss;
1731 ss << "lok::Document::setGraphicSelection(" << pLOEvent->m_nSetGraphicSelectionType;
1732 ss << ", " << pLOEvent->m_nSetGraphicSelectionX;
1733 ss << ", " << pLOEvent->m_nSetGraphicSelectionY << ")";
1734 g_info("%s", ss.str().c_str());
1735 priv->m_pDocument->pClass->setGraphicSelection(priv->m_pDocument,
1736 pLOEvent->m_nSetGraphicSelectionType,
1737 pLOEvent->m_nSetGraphicSelectionX,
1738 pLOEvent->m_nSetGraphicSelectionY);
1741 static void
1742 setClientZoomInThread(gpointer data)
1744 GTask* task = G_TASK(data);
1745 LOKDocView* pDocView = LOK_DOC_VIEW(g_task_get_source_object(task));
1746 LOKDocViewPrivate& priv = getPrivate(pDocView);
1747 LOEvent* pLOEvent = static_cast<LOEvent*>(g_task_get_task_data(task));
1749 priv->m_pDocument->pClass->setClientZoom(priv->m_pDocument,
1750 pLOEvent->m_nTilePixelWidth,
1751 pLOEvent->m_nTilePixelHeight,
1752 pLOEvent->m_nTileTwipWidth,
1753 pLOEvent->m_nTileTwipHeight);
1756 static void
1757 postMouseEventInThread(gpointer data)
1759 GTask* task = G_TASK(data);
1760 LOKDocView* pDocView = LOK_DOC_VIEW(g_task_get_source_object(task));
1761 LOKDocViewPrivate& priv = getPrivate(pDocView);
1762 LOEvent* pLOEvent = static_cast<LOEvent*>(g_task_get_task_data(task));
1764 priv->m_pDocument->pClass->setView(priv->m_pDocument, priv->m_nViewId);
1765 std::stringstream ss;
1766 ss << "lok::Document::postMouseEvent(" << pLOEvent->m_nPostMouseEventType;
1767 ss << ", " << pLOEvent->m_nPostMouseEventX;
1768 ss << ", " << pLOEvent->m_nPostMouseEventY;
1769 ss << ", " << pLOEvent->m_nPostMouseEventCount;
1770 ss << ", " << pLOEvent->m_nPostMouseEventButton;
1771 ss << ", " << pLOEvent->m_nPostMouseEventModifier << ")";
1772 g_info("%s", ss.str().c_str());
1773 priv->m_pDocument->pClass->postMouseEvent(priv->m_pDocument,
1774 pLOEvent->m_nPostMouseEventType,
1775 pLOEvent->m_nPostMouseEventX,
1776 pLOEvent->m_nPostMouseEventY,
1777 pLOEvent->m_nPostMouseEventCount,
1778 pLOEvent->m_nPostMouseEventButton,
1779 pLOEvent->m_nPostMouseEventModifier);
1782 static void
1783 openDocumentInThread (gpointer data)
1785 GTask* task = G_TASK(data);
1786 LOKDocView* pDocView = LOK_DOC_VIEW(g_task_get_source_object(task));
1787 LOKDocViewPrivate& priv = getPrivate(pDocView);
1789 if ( priv->m_pDocument )
1791 priv->m_pDocument->pClass->destroy( priv->m_pDocument );
1792 priv->m_pDocument = nullptr;
1795 priv->m_pOffice->pClass->registerCallback(priv->m_pOffice, globalCallbackWorker, pDocView);
1796 priv->m_pDocument = priv->m_pOffice->pClass->documentLoad( priv->m_pOffice, priv->m_aDocPath );
1797 if ( !priv->m_pDocument )
1799 char *pError = priv->m_pOffice->pClass->getError( priv->m_pOffice );
1800 g_task_return_new_error(task, g_quark_from_static_string ("LOK error"), 0, "%s", pError);
1802 else
1804 gdk_threads_add_idle(postDocumentLoad, pDocView);
1805 g_task_return_boolean (task, true);
1809 static void
1810 setPartInThread(gpointer data)
1812 GTask* task = G_TASK(data);
1813 LOKDocView* pDocView = LOK_DOC_VIEW(g_task_get_source_object(task));
1814 LOKDocViewPrivate& priv = getPrivate(pDocView);
1815 LOEvent* pLOEvent = static_cast<LOEvent*>(g_task_get_task_data(task));
1816 int nPart = pLOEvent->m_nPart;
1818 priv->m_pDocument->pClass->setView(priv->m_pDocument, priv->m_nViewId);
1819 priv->m_pDocument->pClass->setPart( priv->m_pDocument, nPart );
1821 lok_doc_view_reset_view(pDocView);
1824 static void
1825 setPartmodeInThread(gpointer data)
1827 GTask* task = G_TASK(data);
1828 LOKDocView* pDocView = LOK_DOC_VIEW(g_task_get_source_object(task));
1829 LOKDocViewPrivate& priv = getPrivate(pDocView);
1830 LOEvent* pLOEvent = static_cast<LOEvent*>(g_task_get_task_data(task));
1831 int nPartMode = pLOEvent->m_nPartMode;
1833 priv->m_pDocument->pClass->setView(priv->m_pDocument, priv->m_nViewId);
1834 priv->m_pDocument->pClass->setPartMode( priv->m_pDocument, nPartMode );
1837 static void
1838 setEditInThread(gpointer data)
1840 GTask* task = G_TASK(data);
1841 LOKDocView* pDocView = LOK_DOC_VIEW(g_task_get_source_object(task));
1842 LOKDocViewPrivate& priv = getPrivate(pDocView);
1843 LOEvent* pLOEvent = static_cast<LOEvent*>(g_task_get_task_data(task));
1844 gboolean bWasEdit = priv->m_bEdit;
1845 gboolean bEdit = pLOEvent->m_bEdit;
1847 if (!priv->m_bEdit && bEdit)
1848 g_info("lok_doc_view_set_edit: entering edit mode");
1849 else if (priv->m_bEdit && !bEdit)
1851 g_info("lok_doc_view_set_edit: leaving edit mode");
1852 priv->m_pDocument->pClass->setView(priv->m_pDocument, priv->m_nViewId);
1853 priv->m_pDocument->pClass->resetSelection(priv->m_pDocument);
1855 priv->m_bEdit = bEdit;
1856 g_signal_emit(pDocView, doc_view_signals[EDIT_CHANGED], 0, bWasEdit);
1857 gdk_threads_add_idle(queueDraw, GTK_WIDGET(pDocView));
1860 static void
1861 postCommandInThread (gpointer data)
1863 GTask* task = G_TASK(data);
1864 LOKDocView* pDocView = LOK_DOC_VIEW(g_task_get_source_object(task));
1865 LOEvent* pLOEvent = static_cast<LOEvent*>(g_task_get_task_data(task));
1866 LOKDocViewPrivate& priv = getPrivate(pDocView);
1868 priv->m_pDocument->pClass->setView(priv->m_pDocument, priv->m_nViewId);
1869 std::stringstream ss;
1870 ss << "lok::Document::postUnoCommand(" << pLOEvent->m_pCommand << ", " << pLOEvent->m_pArguments << ")";
1871 g_info("%s", ss.str().c_str());
1872 priv->m_pDocument->pClass->postUnoCommand(priv->m_pDocument, pLOEvent->m_pCommand, pLOEvent->m_pArguments, pLOEvent->m_bNotifyWhenFinished);
1875 static void
1876 paintTileInThread (gpointer data)
1878 GTask* task = G_TASK(data);
1879 LOKDocView* pDocView = LOK_DOC_VIEW(g_task_get_source_object(task));
1880 LOKDocViewPrivate& priv = getPrivate(pDocView);
1881 LOEvent* pLOEvent = static_cast<LOEvent*>(g_task_get_task_data(task));
1883 // check if "source" tile buffer is different from "current" tile buffer
1884 if (pLOEvent->m_pTileBuffer != &*priv->m_pTileBuffer)
1886 pLOEvent->m_pTileBuffer = nullptr;
1887 g_task_return_new_error(task,
1888 LOK_TILEBUFFER_ERROR,
1889 LOK_TILEBUFFER_CHANGED,
1890 "TileBuffer has changed");
1891 return;
1893 std::unique_ptr<TileBuffer>& buffer = priv->m_pTileBuffer;
1894 int index = pLOEvent->m_nPaintTileX * buffer->m_nWidth + pLOEvent->m_nPaintTileY;
1895 if (buffer->m_mTiles.find(index) != buffer->m_mTiles.end() &&
1896 buffer->m_mTiles[index].valid)
1897 return;
1899 cairo_surface_t *pSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, nTileSizePixels, nTileSizePixels);
1900 if (cairo_surface_status(pSurface) != CAIRO_STATUS_SUCCESS)
1902 cairo_surface_destroy(pSurface);
1903 g_task_return_new_error(task,
1904 LOK_TILEBUFFER_ERROR,
1905 LOK_TILEBUFFER_MEMORY,
1906 "Error allocating Surface");
1907 return;
1910 unsigned char* pBuffer = cairo_image_surface_get_data(pSurface);
1911 GdkRectangle aTileRectangle;
1912 aTileRectangle.x = pixelToTwip(nTileSizePixels, pLOEvent->m_fPaintTileZoom) * pLOEvent->m_nPaintTileY;
1913 aTileRectangle.y = pixelToTwip(nTileSizePixels, pLOEvent->m_fPaintTileZoom) * pLOEvent->m_nPaintTileX;
1915 priv->m_pDocument->pClass->setView(priv->m_pDocument, priv->m_nViewId);
1916 std::stringstream ss;
1917 GTimer* aTimer = g_timer_new();
1918 gulong nElapsedMs;
1919 ss << "lok::Document::paintTile(" << static_cast<void*>(pBuffer) << ", "
1920 << nTileSizePixels << ", " << nTileSizePixels << ", "
1921 << aTileRectangle.x << ", " << aTileRectangle.y << ", "
1922 << pixelToTwip(nTileSizePixels, pLOEvent->m_fPaintTileZoom) << ", "
1923 << pixelToTwip(nTileSizePixels, pLOEvent->m_fPaintTileZoom) << ")";
1925 priv->m_pDocument->pClass->paintTile(priv->m_pDocument,
1926 pBuffer,
1927 nTileSizePixels, nTileSizePixels,
1928 aTileRectangle.x, aTileRectangle.y,
1929 pixelToTwip(nTileSizePixels, pLOEvent->m_fPaintTileZoom),
1930 pixelToTwip(nTileSizePixels, pLOEvent->m_fPaintTileZoom));
1932 g_timer_elapsed(aTimer, &nElapsedMs);
1933 ss << " rendered in " << (nElapsedMs / 1000.) << " milliseconds";
1934 g_info("%s", ss.str().c_str());
1935 g_timer_destroy(aTimer);
1937 cairo_surface_mark_dirty(pSurface);
1939 // Its likely that while the tilebuffer has changed, one of the paint tile
1940 // requests has passed the previous check at start of this function, and has
1941 // rendered the tile already. We want to stop such rendered tiles from being
1942 // stored in new tile buffer.
1943 if (pLOEvent->m_pTileBuffer != &*priv->m_pTileBuffer)
1945 pLOEvent->m_pTileBuffer = nullptr;
1946 g_task_return_new_error(task,
1947 LOK_TILEBUFFER_ERROR,
1948 LOK_TILEBUFFER_CHANGED,
1949 "TileBuffer has changed");
1950 return;
1953 g_task_return_pointer(task, pSurface, reinterpret_cast<GDestroyNotify>(cairo_surface_destroy));
1957 static void
1958 lokThreadFunc(gpointer data, gpointer /*user_data*/)
1960 GTask* task = G_TASK(data);
1961 LOEvent* pLOEvent = static_cast<LOEvent*>(g_task_get_task_data(task));
1962 LOKDocView* pDocView = LOK_DOC_VIEW(g_task_get_source_object(task));
1963 LOKDocViewPrivate& priv = getPrivate(pDocView);
1965 switch (pLOEvent->m_nType)
1967 case LOK_LOAD_DOC:
1968 openDocumentInThread(task);
1969 break;
1970 case LOK_POST_COMMAND:
1971 postCommandInThread(task);
1972 break;
1973 case LOK_SET_EDIT:
1974 setEditInThread(task);
1975 break;
1976 case LOK_SET_PART:
1977 setPartInThread(task);
1978 break;
1979 case LOK_SET_PARTMODE:
1980 setPartmodeInThread(task);
1981 break;
1982 case LOK_POST_KEY:
1983 // view-only/editable mode already checked during signal key signal emission
1984 postKeyEventInThread(task);
1985 break;
1986 case LOK_PAINT_TILE:
1987 paintTileInThread(task);
1988 break;
1989 case LOK_POST_MOUSE_EVENT:
1990 postMouseEventInThread(task);
1991 break;
1992 case LOK_SET_GRAPHIC_SELECTION:
1993 if (priv->m_bEdit)
1994 setGraphicSelectionInThread(task);
1995 else
1996 g_info ("LOK_SET_GRAPHIC_SELECTION: skipping graphical operation in view-only mode");
1997 break;
1998 case LOK_SET_CLIENT_ZOOM:
1999 setClientZoomInThread(task);
2000 break;
2003 g_object_unref(task);
2006 static void lok_doc_view_init (LOKDocView* pDocView)
2008 LOKDocViewPrivate& priv = getPrivate(pDocView);
2009 priv.m_pImpl = new LOKDocViewPrivateImpl();
2011 gtk_widget_add_events(GTK_WIDGET(pDocView),
2012 GDK_BUTTON_PRESS_MASK
2013 |GDK_BUTTON_RELEASE_MASK
2014 |GDK_BUTTON_MOTION_MASK
2015 |GDK_KEY_PRESS_MASK
2016 |GDK_KEY_RELEASE_MASK);
2018 priv->lokThreadPool = g_thread_pool_new(lokThreadFunc,
2019 nullptr,
2021 FALSE,
2022 nullptr);
2025 static void lok_doc_view_set_property (GObject* object, guint propId, const GValue *value, GParamSpec *pspec)
2027 LOKDocView* pDocView = LOK_DOC_VIEW (object);
2028 LOKDocViewPrivate& priv = getPrivate(pDocView);
2029 gboolean bDocPasswordEnabled = priv->m_nLOKFeatures & LOK_FEATURE_DOCUMENT_PASSWORD;
2030 gboolean bDocPasswordToModifyEnabled = priv->m_nLOKFeatures & LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY;
2032 switch (propId)
2034 case PROP_LO_PATH:
2035 priv->m_aLOPath = g_value_dup_string (value);
2036 break;
2037 case PROP_LO_POINTER:
2038 priv->m_pOffice = static_cast<LibreOfficeKit*>(g_value_get_pointer(value));
2039 break;
2040 case PROP_USER_PROFILE_URL:
2041 priv->m_pUserProfileURL = g_value_dup_string(value);
2042 break;
2043 case PROP_DOC_PATH:
2044 priv->m_aDocPath = g_value_dup_string (value);
2045 break;
2046 case PROP_DOC_POINTER:
2047 priv->m_pDocument = static_cast<LibreOfficeKitDocument*>(g_value_get_pointer(value));
2048 break;
2049 case PROP_EDITABLE:
2050 lok_doc_view_set_edit (pDocView, g_value_get_boolean (value));
2051 break;
2052 case PROP_ZOOM:
2053 lok_doc_view_set_zoom (pDocView, g_value_get_float (value));
2054 break;
2055 case PROP_DOC_WIDTH:
2056 priv->m_nDocumentWidthTwips = g_value_get_long (value);
2057 break;
2058 case PROP_DOC_HEIGHT:
2059 priv->m_nDocumentHeightTwips = g_value_get_long (value);
2060 break;
2061 case PROP_DOC_PASSWORD:
2062 if (g_value_get_boolean (value) != bDocPasswordEnabled)
2064 priv->m_nLOKFeatures = priv->m_nLOKFeatures ^ LOK_FEATURE_DOCUMENT_PASSWORD;
2065 priv->m_pOffice->pClass->setOptionalFeatures(priv->m_pOffice, priv->m_nLOKFeatures);
2067 break;
2068 case PROP_DOC_PASSWORD_TO_MODIFY:
2069 if ( g_value_get_boolean (value) != bDocPasswordToModifyEnabled)
2071 priv->m_nLOKFeatures = priv->m_nLOKFeatures ^ LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY;
2072 priv->m_pOffice->pClass->setOptionalFeatures(priv->m_pOffice, priv->m_nLOKFeatures);
2074 break;
2075 default:
2076 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propId, pspec);
2080 static void lok_doc_view_get_property (GObject* object, guint propId, GValue *value, GParamSpec *pspec)
2082 LOKDocView* pDocView = LOK_DOC_VIEW (object);
2083 LOKDocViewPrivate& priv = getPrivate(pDocView);
2085 switch (propId)
2087 case PROP_LO_PATH:
2088 g_value_set_string (value, priv->m_aLOPath);
2089 break;
2090 case PROP_LO_POINTER:
2091 g_value_set_pointer(value, priv->m_pOffice);
2092 break;
2093 case PROP_USER_PROFILE_URL:
2094 g_value_set_string(value, priv->m_pUserProfileURL);
2095 break;
2096 case PROP_DOC_PATH:
2097 g_value_set_string (value, priv->m_aDocPath);
2098 break;
2099 case PROP_DOC_POINTER:
2100 g_value_set_pointer(value, priv->m_pDocument);
2101 break;
2102 case PROP_EDITABLE:
2103 g_value_set_boolean (value, priv->m_bEdit);
2104 break;
2105 case PROP_LOAD_PROGRESS:
2106 g_value_set_double (value, priv->m_nLoadProgress);
2107 break;
2108 case PROP_ZOOM:
2109 g_value_set_float (value, priv->m_fZoom);
2110 break;
2111 case PROP_IS_LOADING:
2112 g_value_set_boolean (value, priv->m_bIsLoading);
2113 break;
2114 case PROP_DOC_WIDTH:
2115 g_value_set_long (value, priv->m_nDocumentWidthTwips);
2116 break;
2117 case PROP_DOC_HEIGHT:
2118 g_value_set_long (value, priv->m_nDocumentHeightTwips);
2119 break;
2120 case PROP_CAN_ZOOM_IN:
2121 g_value_set_boolean (value, priv->m_bCanZoomIn);
2122 break;
2123 case PROP_CAN_ZOOM_OUT:
2124 g_value_set_boolean (value, priv->m_bCanZoomOut);
2125 break;
2126 case PROP_DOC_PASSWORD:
2127 g_value_set_boolean (value, priv->m_nLOKFeatures & LOK_FEATURE_DOCUMENT_PASSWORD);
2128 break;
2129 case PROP_DOC_PASSWORD_TO_MODIFY:
2130 g_value_set_boolean (value, priv->m_nLOKFeatures & LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY);
2131 break;
2132 default:
2133 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propId, pspec);
2137 static gboolean lok_doc_view_draw (GtkWidget* pWidget, cairo_t* pCairo)
2139 LOKDocView *pDocView = LOK_DOC_VIEW (pWidget);
2141 renderDocument (pDocView, pCairo);
2142 renderOverlay (pDocView, pCairo);
2144 return FALSE;
2147 static void lok_doc_view_finalize (GObject* object)
2149 LOKDocView* pDocView = LOK_DOC_VIEW (object);
2150 LOKDocViewPrivate& priv = getPrivate(pDocView);
2152 if (priv->m_pDocument)
2153 priv->m_pDocument->pClass->destroy (priv->m_pDocument);
2154 if (priv->m_pOffice)
2155 priv->m_pOffice->pClass->destroy (priv->m_pOffice);
2156 delete priv.m_pImpl;
2157 priv.m_pImpl = nullptr;
2159 G_OBJECT_CLASS (lok_doc_view_parent_class)->finalize (object);
2162 static gboolean lok_doc_view_initable_init (GInitable *initable, GCancellable* /*cancellable*/, GError **error)
2164 LOKDocView *pDocView = LOK_DOC_VIEW (initable);
2165 LOKDocViewPrivate& priv = getPrivate(pDocView);
2167 if (priv->m_pOffice != nullptr)
2168 return TRUE;
2170 priv->m_pOffice = lok_init_2(priv->m_aLOPath, priv->m_pUserProfileURL);
2172 if (priv->m_pOffice == nullptr)
2174 g_set_error (error,
2175 g_quark_from_static_string ("LOK initialization error"), 0,
2176 "Failed to get LibreOfficeKit context. Make sure path (%s) is correct",
2177 priv->m_aLOPath);
2178 return FALSE;
2181 return TRUE;
2184 static void lok_doc_view_initable_iface_init (GInitableIface *iface)
2186 iface->init = lok_doc_view_initable_init;
2189 static void lok_doc_view_class_init (LOKDocViewClass* pClass)
2191 GObjectClass *pGObjectClass = G_OBJECT_CLASS(pClass);
2192 GtkWidgetClass *pWidgetClass = GTK_WIDGET_CLASS(pClass);
2194 pGObjectClass->get_property = lok_doc_view_get_property;
2195 pGObjectClass->set_property = lok_doc_view_set_property;
2196 pGObjectClass->finalize = lok_doc_view_finalize;
2198 pWidgetClass->draw = lok_doc_view_draw;
2199 pWidgetClass->button_press_event = lok_doc_view_signal_button;
2200 pWidgetClass->button_release_event = lok_doc_view_signal_button;
2201 pWidgetClass->key_press_event = signalKey;
2202 pWidgetClass->key_release_event = signalKey;
2203 pWidgetClass->motion_notify_event = lok_doc_view_signal_motion;
2206 * LOKDocView:lopath:
2208 * The absolute path of the LibreOffice install.
2210 properties[PROP_LO_PATH] =
2211 g_param_spec_string("lopath",
2212 "LO Path",
2213 "LibreOffice Install Path",
2214 nullptr,
2215 static_cast<GParamFlags>(G_PARAM_READWRITE |
2216 G_PARAM_CONSTRUCT_ONLY |
2217 G_PARAM_STATIC_STRINGS));
2220 * LOKDocView:lopointer:
2222 * A LibreOfficeKit* in case lok_init() is already called
2223 * previously.
2225 properties[PROP_LO_POINTER] =
2226 g_param_spec_pointer("lopointer",
2227 "LO Pointer",
2228 "A LibreOfficeKit* from lok_init()",
2229 static_cast<GParamFlags>(G_PARAM_READWRITE |
2230 G_PARAM_CONSTRUCT_ONLY |
2231 G_PARAM_STATIC_STRINGS));
2234 * LOKDocView:userprofileurl:
2236 * The absolute path of the LibreOffice user profile.
2238 properties[PROP_USER_PROFILE_URL] =
2239 g_param_spec_string("userprofileurl",
2240 "User profile path",
2241 "LibreOffice user profile path",
2242 nullptr,
2243 static_cast<GParamFlags>(G_PARAM_READWRITE |
2244 G_PARAM_CONSTRUCT_ONLY |
2245 G_PARAM_STATIC_STRINGS));
2248 * LOKDocView:docpath:
2250 * The path of the document that is currently being viewed.
2252 properties[PROP_DOC_PATH] =
2253 g_param_spec_string("docpath",
2254 "Document Path",
2255 "The URI of the document to open",
2256 nullptr,
2257 static_cast<GParamFlags>(G_PARAM_READWRITE |
2258 G_PARAM_STATIC_STRINGS));
2261 * LOKDocView:docpointer:
2263 * A LibreOfficeKitDocument* in case documentLoad() is already called
2264 * previously.
2266 properties[PROP_DOC_POINTER] =
2267 g_param_spec_pointer("docpointer",
2268 "Document Pointer",
2269 "A LibreOfficeKitDocument* from documentLoad()",
2270 static_cast<GParamFlags>(G_PARAM_READWRITE |
2271 G_PARAM_STATIC_STRINGS));
2274 * LOKDocView:editable:
2276 * Whether the document loaded inside of #LOKDocView is editable or not.
2278 properties[PROP_EDITABLE] =
2279 g_param_spec_boolean("editable",
2280 "Editable",
2281 "Whether the content is in edit mode or not",
2282 FALSE,
2283 static_cast<GParamFlags>(G_PARAM_READWRITE |
2284 G_PARAM_STATIC_STRINGS));
2287 * LOKDocView:load-progress:
2289 * The percent completion of the current loading operation of the
2290 * document. This can be used for progress bars. Note that this is not a
2291 * very accurate progress indicator, and its value might reset it couple of
2292 * times to 0 and start again. You should not rely on its numbers.
2294 properties[PROP_LOAD_PROGRESS] =
2295 g_param_spec_double("load-progress",
2296 "Estimated Load Progress",
2297 "Shows the progress of the document load operation",
2298 0.0, 1.0, 0.0,
2299 static_cast<GParamFlags>(G_PARAM_READABLE |
2300 G_PARAM_STATIC_STRINGS));
2303 * LOKDocView:zoom-level:
2305 * The current zoom level of the document loaded inside #LOKDocView. The
2306 * default value is 1.0.
2308 properties[PROP_ZOOM] =
2309 g_param_spec_float("zoom-level",
2310 "Zoom Level",
2311 "The current zoom level of the content",
2312 0, 5.0, 1.0,
2313 static_cast<GParamFlags>(G_PARAM_READWRITE |
2314 G_PARAM_STATIC_STRINGS));
2317 * LOKDocView:is-loading:
2319 * Whether the requested document is being loaded or not. %TRUE if it is
2320 * being loaded, otherwise %FALSE.
2322 properties[PROP_IS_LOADING] =
2323 g_param_spec_boolean("is-loading",
2324 "Is Loading",
2325 "Whether the view is loading a document",
2326 FALSE,
2327 static_cast<GParamFlags>(G_PARAM_READABLE |
2328 G_PARAM_STATIC_STRINGS));
2331 * LOKDocView:doc-width:
2333 * The width of the currently loaded document in #LOKDocView in twips.
2335 properties[PROP_DOC_WIDTH] =
2336 g_param_spec_long("doc-width",
2337 "Document Width",
2338 "Width of the document in twips",
2339 0, G_MAXLONG, 0,
2340 static_cast<GParamFlags>(G_PARAM_READWRITE |
2341 G_PARAM_STATIC_STRINGS));
2344 * LOKDocView:doc-height:
2346 * The height of the currently loaded document in #LOKDocView in twips.
2348 properties[PROP_DOC_HEIGHT] =
2349 g_param_spec_long("doc-height",
2350 "Document Height",
2351 "Height of the document in twips",
2352 0, G_MAXLONG, 0,
2353 static_cast<GParamFlags>(G_PARAM_READWRITE |
2354 G_PARAM_STATIC_STRINGS));
2357 * LOKDocView:can-zoom-in:
2359 * It tells whether the view can further be zoomed in or not.
2361 properties[PROP_CAN_ZOOM_IN] =
2362 g_param_spec_boolean("can-zoom-in",
2363 "Can Zoom In",
2364 "Whether the view can be zoomed in further",
2365 TRUE,
2366 static_cast<GParamFlags>(G_PARAM_READABLE
2367 | G_PARAM_STATIC_STRINGS));
2370 * LOKDocView:can-zoom-out:
2372 * It tells whether the view can further be zoomed out or not.
2374 properties[PROP_CAN_ZOOM_OUT] =
2375 g_param_spec_boolean("can-zoom-out",
2376 "Can Zoom Out",
2377 "Whether the view can be zoomed out further",
2378 TRUE,
2379 static_cast<GParamFlags>(G_PARAM_READABLE
2380 | G_PARAM_STATIC_STRINGS));
2383 * LOKDocView:doc-password:
2385 * Set it to true if client supports providing password for viewing
2386 * password protected documents
2388 properties[PROP_DOC_PASSWORD] =
2389 g_param_spec_boolean("doc-password",
2390 "Document password capability",
2391 "Whether client supports providing document passwords",
2392 FALSE,
2393 static_cast<GParamFlags>(G_PARAM_READWRITE
2394 | G_PARAM_STATIC_STRINGS));
2397 * LOKDocView:doc-password-to-modify:
2399 * Set it to true if client supports providing password for edit-protected documents
2401 properties[PROP_DOC_PASSWORD_TO_MODIFY] =
2402 g_param_spec_boolean("doc-password-to-modify",
2403 "Edit document password capability",
2404 "Whether the client supports providing passwords to edit documents",
2405 FALSE,
2406 static_cast<GParamFlags>(G_PARAM_READWRITE
2407 | G_PARAM_STATIC_STRINGS));
2409 g_object_class_install_properties(pGObjectClass, PROP_LAST, properties);
2412 * LOKDocView::load-changed:
2413 * @pDocView: the #LOKDocView on which the signal is emitted
2414 * @fLoadProgress: the new progress value
2416 doc_view_signals[LOAD_CHANGED] =
2417 g_signal_new("load-changed",
2418 G_TYPE_FROM_CLASS (pGObjectClass),
2419 G_SIGNAL_RUN_FIRST,
2421 nullptr, nullptr,
2422 g_cclosure_marshal_VOID__DOUBLE,
2423 G_TYPE_NONE, 1,
2424 G_TYPE_DOUBLE);
2427 * LOKDocView::edit-changed:
2428 * @pDocView: the #LOKDocView on which the signal is emitted
2429 * @bEdit: the new edit value of the view
2431 doc_view_signals[EDIT_CHANGED] =
2432 g_signal_new("edit-changed",
2433 G_TYPE_FROM_CLASS (pGObjectClass),
2434 G_SIGNAL_RUN_FIRST,
2436 nullptr, nullptr,
2437 g_cclosure_marshal_VOID__BOOLEAN,
2438 G_TYPE_NONE, 1,
2439 G_TYPE_BOOLEAN);
2442 * LOKDocView::command-changed:
2443 * @pDocView: the #LOKDocView on which the signal is emitted
2444 * @aCommand: the command that was changed
2446 doc_view_signals[COMMAND_CHANGED] =
2447 g_signal_new("command-changed",
2448 G_TYPE_FROM_CLASS(pGObjectClass),
2449 G_SIGNAL_RUN_FIRST,
2451 nullptr, nullptr,
2452 g_cclosure_marshal_VOID__STRING,
2453 G_TYPE_NONE, 1,
2454 G_TYPE_STRING);
2457 * LOKDocView::search-not-found:
2458 * @pDocView: the #LOKDocView on which the signal is emitted
2459 * @aCommand: the string for which the search was not found.
2461 doc_view_signals[SEARCH_NOT_FOUND] =
2462 g_signal_new("search-not-found",
2463 G_TYPE_FROM_CLASS(pGObjectClass),
2464 G_SIGNAL_RUN_FIRST,
2466 nullptr, nullptr,
2467 g_cclosure_marshal_VOID__STRING,
2468 G_TYPE_NONE, 1,
2469 G_TYPE_STRING);
2472 * LOKDocView::part-changed:
2473 * @pDocView: the #LOKDocView on which the signal is emitted
2474 * @aCommand: the part number which the view changed to
2476 doc_view_signals[PART_CHANGED] =
2477 g_signal_new("part-changed",
2478 G_TYPE_FROM_CLASS(pGObjectClass),
2479 G_SIGNAL_RUN_FIRST,
2481 nullptr, nullptr,
2482 g_cclosure_marshal_VOID__INT,
2483 G_TYPE_NONE, 1,
2484 G_TYPE_INT);
2487 * LOKDocView::size-changed:
2488 * @pDocView: the #LOKDocView on which the signal is emitted
2489 * @aCommand: NULL, we just notify that want to notify the UI elements that are interested.
2491 doc_view_signals[SIZE_CHANGED] =
2492 g_signal_new("size-changed",
2493 G_TYPE_FROM_CLASS(pGObjectClass),
2494 G_SIGNAL_RUN_FIRST,
2496 nullptr, nullptr,
2497 g_cclosure_marshal_VOID__VOID,
2498 G_TYPE_NONE, 1,
2499 G_TYPE_INT);
2502 * LOKDocView::hyperlinked-clicked:
2503 * @pDocView: the #LOKDocView on which the signal is emitted
2504 * @aHyperlink: the URI which the application should handle
2506 doc_view_signals[HYPERLINK_CLICKED] =
2507 g_signal_new("hyperlink-clicked",
2508 G_TYPE_FROM_CLASS(pGObjectClass),
2509 G_SIGNAL_RUN_FIRST,
2511 nullptr, nullptr,
2512 g_cclosure_marshal_VOID__STRING,
2513 G_TYPE_NONE, 1,
2514 G_TYPE_STRING);
2517 * LOKDocView::cursor-changed:
2518 * @pDocView: the #LOKDocView on which the signal is emitted
2519 * @nX: The new cursor position (X coordinate) in pixels
2520 * @nY: The new cursor position (Y coordinate) in pixels
2521 * @nWidth: The width of new cursor
2522 * @nHeight: The height of new cursor
2524 doc_view_signals[CURSOR_CHANGED] =
2525 g_signal_new("cursor-changed",
2526 G_TYPE_FROM_CLASS(pGObjectClass),
2527 G_SIGNAL_RUN_FIRST,
2529 nullptr, nullptr,
2530 g_cclosure_marshal_generic,
2531 G_TYPE_NONE, 4,
2532 G_TYPE_INT, G_TYPE_INT,
2533 G_TYPE_INT, G_TYPE_INT);
2536 * LOKDocView::search-result-count:
2537 * @pDocView: the #LOKDocView on which the signal is emitted
2538 * @aCommand: number of matches.
2540 doc_view_signals[SEARCH_RESULT_COUNT] =
2541 g_signal_new("search-result-count",
2542 G_TYPE_FROM_CLASS(pGObjectClass),
2543 G_SIGNAL_RUN_FIRST,
2545 nullptr, nullptr,
2546 g_cclosure_marshal_VOID__STRING,
2547 G_TYPE_NONE, 1,
2548 G_TYPE_STRING);
2551 * LOKDocView::command-result:
2552 * @pDocView: the #LOKDocView on which the signal is emitted
2553 * @aCommand: JSON containing the info about the command that finished,
2554 * and its success status.
2556 doc_view_signals[COMMAND_RESULT] =
2557 g_signal_new("command-result",
2558 G_TYPE_FROM_CLASS(pGObjectClass),
2559 G_SIGNAL_RUN_FIRST,
2561 nullptr, nullptr,
2562 g_cclosure_marshal_VOID__STRING,
2563 G_TYPE_NONE, 1,
2564 G_TYPE_STRING);
2567 * LOKDocView::formula-changed:
2568 * @pDocView: the #LOKDocView on which the signal is emitted
2569 * @aCommand: formula text content
2571 doc_view_signals[FORMULA_CHANGED] =
2572 g_signal_new("formula-changed",
2573 G_TYPE_FROM_CLASS(pGObjectClass),
2574 G_SIGNAL_RUN_FIRST,
2576 nullptr, nullptr,
2577 g_cclosure_marshal_VOID__STRING,
2578 G_TYPE_NONE, 1,
2579 G_TYPE_STRING);
2582 * LOKDocView::text-selection:
2583 * @pDocView: the #LOKDocView on which the signal is emitted
2584 * @bIsTextSelected: whether text selected is non-null
2586 doc_view_signals[TEXT_SELECTION] =
2587 g_signal_new("text-selection",
2588 G_TYPE_FROM_CLASS(pGObjectClass),
2589 G_SIGNAL_RUN_FIRST,
2591 nullptr, nullptr,
2592 g_cclosure_marshal_VOID__BOOLEAN,
2593 G_TYPE_NONE, 1,
2594 G_TYPE_BOOLEAN);
2597 * LOKDocView::password-required:
2598 * @pDocView: the #LOKDocView on which the signal is emitted
2599 * @pUrl: URL of the document for which password is required
2600 * @bModify: whether password id required to modify the document
2601 * This is true when password is required to edit the document,
2602 * while it can still be viewed without password. In such cases, provide a NULL
2603 * password for read-only access to the document.
2604 * If false, password is required for opening the document, and document
2605 * cannot be opened without providing a valid password.
2607 * Password must be provided by calling lok_doc_view_set_document_password
2608 * function with pUrl as provided by the callback.
2610 * Upon entering a invalid password, another `password-required` signal is
2611 * emitted.
2612 * Upon entering a valid password, document starts to load.
2613 * Upon entering a NULL password: if bModify is %TRUE, document starts to
2614 * open in view-only mode, else loading of document is aborted.
2616 doc_view_signals[PASSWORD_REQUIRED] =
2617 g_signal_new("password-required",
2618 G_TYPE_FROM_CLASS(pGObjectClass),
2619 G_SIGNAL_RUN_FIRST,
2621 nullptr, nullptr,
2622 g_cclosure_marshal_generic,
2623 G_TYPE_NONE, 2,
2624 G_TYPE_STRING,
2625 G_TYPE_BOOLEAN);
2628 SAL_DLLPUBLIC_EXPORT GtkWidget*
2629 lok_doc_view_new (const gchar* pPath, GCancellable *cancellable, GError **error)
2631 return GTK_WIDGET (g_initable_new (LOK_TYPE_DOC_VIEW, cancellable, error,
2632 "lopath", pPath == nullptr ? LOK_PATH : pPath,
2633 "halign", GTK_ALIGN_CENTER,
2634 "valign", GTK_ALIGN_CENTER,
2635 nullptr));
2638 SAL_DLLPUBLIC_EXPORT GtkWidget*
2639 lok_doc_view_new_from_user_profile (const gchar* pPath, const gchar* pUserProfile, GCancellable *cancellable, GError **error)
2641 return GTK_WIDGET(g_initable_new(LOK_TYPE_DOC_VIEW, cancellable, error,
2642 "lopath", pPath == nullptr ? LOK_PATH : pPath,
2643 "userprofileurl", pUserProfile,
2644 "halign", GTK_ALIGN_CENTER,
2645 "valign", GTK_ALIGN_CENTER,
2646 nullptr));
2649 SAL_DLLPUBLIC_EXPORT GtkWidget* lok_doc_view_new_from_widget(LOKDocView* pOldLOKDocView)
2651 LOKDocViewPrivate& pOldPriv = getPrivate(pOldLOKDocView);
2652 GtkWidget* pNewDocView = GTK_WIDGET(g_initable_new(LOK_TYPE_DOC_VIEW, /*cancellable=*/nullptr, /*error=*/nullptr,
2653 "lopath", pOldPriv->m_aLOPath,
2654 "userprofileurl", pOldPriv->m_pUserProfileURL,
2655 "lopointer", pOldPriv->m_pOffice,
2656 "docpointer", pOldPriv->m_pDocument,
2657 "halign", GTK_ALIGN_CENTER,
2658 "valign", GTK_ALIGN_CENTER,
2659 nullptr));
2661 // No documentLoad(), just a createView().
2662 LibreOfficeKitDocument* pDocument = lok_doc_view_get_document(LOK_DOC_VIEW(pNewDocView));
2663 LOKDocViewPrivate& pNewPriv = getPrivate(LOK_DOC_VIEW(pNewDocView));
2664 pNewPriv->m_nViewId = pDocument->pClass->createView(pDocument);
2665 pNewPriv->m_aRenderingArguments = pOldPriv->m_aRenderingArguments;
2667 postDocumentLoad(pNewDocView);
2668 return pNewDocView;
2671 SAL_DLLPUBLIC_EXPORT gboolean
2672 lok_doc_view_open_document_finish (LOKDocView* pDocView, GAsyncResult* res, GError** error)
2674 GTask* task = G_TASK(res);
2676 g_return_val_if_fail(g_task_is_valid(res, pDocView), false);
2677 g_return_val_if_fail(g_task_get_source_tag(task) == lok_doc_view_open_document, false);
2678 g_return_val_if_fail(error == nullptr || *error == nullptr, false);
2680 return g_task_propagate_boolean(task, error);
2683 SAL_DLLPUBLIC_EXPORT void
2684 lok_doc_view_open_document (LOKDocView* pDocView,
2685 const gchar* pPath,
2686 const gchar* pRenderingArguments,
2687 GCancellable* cancellable,
2688 GAsyncReadyCallback callback,
2689 gpointer userdata)
2691 GTask* task = g_task_new(pDocView, cancellable, callback, userdata);
2692 LOKDocViewPrivate& priv = getPrivate(pDocView);
2693 GError* error = nullptr;
2695 LOEvent* pLOEvent = new LOEvent(LOK_LOAD_DOC);
2696 pLOEvent->m_pPath = pPath;
2698 priv->m_aDocPath = pPath;
2699 if (pRenderingArguments)
2700 priv->m_aRenderingArguments = pRenderingArguments;
2701 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
2702 g_task_set_source_tag(task, reinterpret_cast<gpointer>(lok_doc_view_open_document));
2704 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
2705 if (error != nullptr)
2707 g_warning("Unable to call LOK_LOAD_DOC: %s", error->message);
2708 g_clear_error(&error);
2710 g_object_unref(task);
2713 SAL_DLLPUBLIC_EXPORT LibreOfficeKitDocument*
2714 lok_doc_view_get_document (LOKDocView* pDocView)
2716 LOKDocViewPrivate& priv = getPrivate(pDocView);
2717 return priv->m_pDocument;
2720 SAL_DLLPUBLIC_EXPORT void
2721 lok_doc_view_set_visible_area (LOKDocView* pDocView, GdkRectangle* pVisibleArea)
2723 if (!pVisibleArea)
2724 return;
2726 LOKDocViewPrivate& priv = getPrivate(pDocView);
2727 priv->m_aVisibleArea = *pVisibleArea;
2728 priv->m_bVisibleAreaSet = true;
2731 SAL_DLLPUBLIC_EXPORT void
2732 lok_doc_view_set_zoom (LOKDocView* pDocView, float fZoom)
2734 LOKDocViewPrivate& priv = getPrivate(pDocView);
2735 GError* error = nullptr;
2737 if (!priv->m_pDocument)
2738 return;
2740 // Clamp the input value in [MIN_ZOOM, MAX_ZOOM]
2741 fZoom = fZoom < MIN_ZOOM ? MIN_ZOOM : fZoom;
2742 fZoom = fZoom > MAX_ZOOM ? MAX_ZOOM : fZoom;
2744 if (rtl::math::approxEqual(fZoom, priv->m_fZoom))
2745 return;
2747 priv->m_fZoom = fZoom;
2748 long nDocumentWidthPixels = twipToPixel(priv->m_nDocumentWidthTwips, fZoom);
2749 long nDocumentHeightPixels = twipToPixel(priv->m_nDocumentHeightTwips, fZoom);
2750 // Total number of columns in this document.
2751 guint nColumns = ceil((double)nDocumentWidthPixels / nTileSizePixels);
2753 priv->m_pTileBuffer = std::unique_ptr<TileBuffer>(new TileBuffer(priv->m_pDocument,
2754 nColumns));
2755 gtk_widget_set_size_request(GTK_WIDGET(pDocView),
2756 nDocumentWidthPixels,
2757 nDocumentHeightPixels);
2759 g_object_notify_by_pspec(G_OBJECT(pDocView), properties[PROP_ZOOM]);
2761 // set properties to indicate if view can be further zoomed in/out
2762 bool bCanZoomIn = priv->m_fZoom < MAX_ZOOM;
2763 bool bCanZoomOut = priv->m_fZoom > MIN_ZOOM;
2764 if (bCanZoomIn != bool(priv->m_bCanZoomIn))
2766 priv->m_bCanZoomIn = bCanZoomIn;
2767 g_object_notify_by_pspec(G_OBJECT(pDocView), properties[PROP_CAN_ZOOM_IN]);
2769 if (bCanZoomOut != bool(priv->m_bCanZoomOut))
2771 priv->m_bCanZoomOut = bCanZoomOut;
2772 g_object_notify_by_pspec(G_OBJECT(pDocView), properties[PROP_CAN_ZOOM_OUT]);
2775 // Update the client's view size
2776 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
2777 LOEvent* pLOEvent = new LOEvent(LOK_SET_CLIENT_ZOOM);
2778 pLOEvent->m_nTilePixelWidth = nTileSizePixels;
2779 pLOEvent->m_nTilePixelHeight = nTileSizePixels;
2780 pLOEvent->m_nTileTwipWidth = pixelToTwip(nTileSizePixels, fZoom);
2781 pLOEvent->m_nTileTwipHeight = pixelToTwip(nTileSizePixels, fZoom);
2782 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
2784 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
2785 if (error != nullptr)
2787 g_warning("Unable to call LOK_SET_CLIENT_ZOOM: %s", error->message);
2788 g_clear_error(&error);
2790 g_object_unref(task);
2792 priv->m_nTileSizeTwips = pixelToTwip(nTileSizePixels, priv->m_fZoom);
2795 SAL_DLLPUBLIC_EXPORT gfloat
2796 lok_doc_view_get_zoom (LOKDocView* pDocView)
2798 LOKDocViewPrivate& priv = getPrivate(pDocView);
2799 return priv->m_fZoom;
2802 SAL_DLLPUBLIC_EXPORT gint
2803 lok_doc_view_get_parts (LOKDocView* pDocView)
2805 LOKDocViewPrivate& priv = getPrivate(pDocView);
2806 if (!priv->m_pDocument)
2807 return -1;
2809 priv->m_pDocument->pClass->setView(priv->m_pDocument, priv->m_nViewId);
2810 return priv->m_pDocument->pClass->getParts( priv->m_pDocument );
2813 SAL_DLLPUBLIC_EXPORT gint
2814 lok_doc_view_get_part (LOKDocView* pDocView)
2816 LOKDocViewPrivate& priv = getPrivate(pDocView);
2817 if (!priv->m_pDocument)
2818 return -1;
2820 priv->m_pDocument->pClass->setView(priv->m_pDocument, priv->m_nViewId);
2821 return priv->m_pDocument->pClass->getPart( priv->m_pDocument );
2824 SAL_DLLPUBLIC_EXPORT void
2825 lok_doc_view_set_part (LOKDocView* pDocView, int nPart)
2827 LOKDocViewPrivate& priv = getPrivate(pDocView);
2828 if (!priv->m_pDocument)
2829 return;
2831 if (nPart < 0 || nPart >= priv->m_nParts)
2833 g_warning("Invalid part request : %d", nPart);
2834 return;
2837 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
2838 LOEvent* pLOEvent = new LOEvent(LOK_SET_PART);
2839 GError* error = nullptr;
2841 pLOEvent->m_nPart = nPart;
2842 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
2844 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
2845 if (error != nullptr)
2847 g_warning("Unable to call LOK_SET_PART: %s", error->message);
2848 g_clear_error(&error);
2850 g_object_unref(task);
2853 SAL_DLLPUBLIC_EXPORT gchar*
2854 lok_doc_view_get_part_name (LOKDocView* pDocView, int nPart)
2856 LOKDocViewPrivate& priv = getPrivate(pDocView);
2857 if (!priv->m_pDocument)
2858 return nullptr;
2860 priv->m_pDocument->pClass->setView(priv->m_pDocument, priv->m_nViewId);
2861 return priv->m_pDocument->pClass->getPartName( priv->m_pDocument, nPart );
2864 SAL_DLLPUBLIC_EXPORT void
2865 lok_doc_view_set_partmode(LOKDocView* pDocView,
2866 int nPartMode)
2868 LOKDocViewPrivate& priv = getPrivate(pDocView);
2869 if (!priv->m_pDocument)
2870 return;
2872 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
2873 LOEvent* pLOEvent = new LOEvent(LOK_SET_PARTMODE);
2874 GError* error = nullptr;
2876 pLOEvent->m_nPartMode = nPartMode;
2877 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
2879 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
2880 if (error != nullptr)
2882 g_warning("Unable to call LOK_SET_PARTMODE: %s", error->message);
2883 g_clear_error(&error);
2885 g_object_unref(task);
2888 SAL_DLLPUBLIC_EXPORT void
2889 lok_doc_view_reset_view(LOKDocView* pDocView)
2891 LOKDocViewPrivate& priv = getPrivate(pDocView);
2893 if (priv->m_pTileBuffer != nullptr)
2894 priv->m_pTileBuffer->resetAllTiles();
2895 priv->m_nLoadProgress = 0.0;
2897 memset(&priv->m_aVisibleCursor, 0, sizeof(priv->m_aVisibleCursor));
2898 priv->m_bCursorOverlayVisible = false;
2899 priv->m_bCursorVisible = false;
2901 priv->m_nLastButtonPressTime = 0;
2902 priv->m_nLastButtonReleaseTime = 0;
2903 priv->m_aTextSelectionRectangles.clear();
2905 memset(&priv->m_aTextSelectionStart, 0, sizeof(priv->m_aTextSelectionStart));
2906 memset(&priv->m_aTextSelectionEnd, 0, sizeof(priv->m_aTextSelectionEnd));
2907 memset(&priv->m_aGraphicSelection, 0, sizeof(priv->m_aGraphicSelection));
2908 priv->m_bInDragGraphicSelection = false;
2909 memset(&priv->m_aCellCursor, 0, sizeof(priv->m_aCellCursor));
2911 cairo_surface_destroy(priv->m_pHandleStart);
2912 priv->m_pHandleStart = nullptr;
2913 memset(&priv->m_aHandleStartRect, 0, sizeof(priv->m_aHandleStartRect));
2914 priv->m_bInDragStartHandle = false;
2916 cairo_surface_destroy(priv->m_pHandleMiddle);
2917 priv->m_pHandleMiddle = nullptr;
2918 memset(&priv->m_aHandleMiddleRect, 0, sizeof(priv->m_aHandleMiddleRect));
2919 priv->m_bInDragMiddleHandle = false;
2921 cairo_surface_destroy(priv->m_pHandleEnd);
2922 priv->m_pHandleEnd = nullptr;
2923 memset(&priv->m_aHandleEndRect, 0, sizeof(priv->m_aHandleEndRect));
2924 priv->m_bInDragEndHandle = false;
2926 cairo_surface_destroy(priv->m_pGraphicHandle);
2927 priv->m_pGraphicHandle = nullptr;
2928 memset(&priv->m_aGraphicHandleRects, 0, sizeof(priv->m_aGraphicHandleRects));
2929 memset(&priv->m_bInDragGraphicHandles, 0, sizeof(priv->m_bInDragGraphicHandles));
2931 priv->m_nViewId = 0;
2933 gtk_widget_queue_draw(GTK_WIDGET(pDocView));
2936 SAL_DLLPUBLIC_EXPORT void
2937 lok_doc_view_set_edit(LOKDocView* pDocView,
2938 gboolean bEdit)
2940 LOKDocViewPrivate& priv = getPrivate(pDocView);
2941 if (!priv->m_pDocument)
2942 return;
2944 GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr);
2945 LOEvent* pLOEvent = new LOEvent(LOK_SET_EDIT);
2946 GError* error = nullptr;
2948 pLOEvent->m_bEdit = bEdit;
2949 g_task_set_task_data(task, pLOEvent, LOEvent::destroy);
2951 g_thread_pool_push(priv->lokThreadPool, g_object_ref(task), &error);
2952 if (error != nullptr)
2954 g_warning("Unable to call LOK_SET_EDIT: %s", error->message);
2955 g_clear_error(&error);
2957 g_object_unref(task);
2960 SAL_DLLPUBLIC_EXPORT gboolean
2961 lok_doc_view_get_edit (LOKDocView* pDocView)
2963 LOKDocViewPrivate& priv = getPrivate(pDocView);
2964 return priv->m_bEdit;
2967 SAL_DLLPUBLIC_EXPORT void
2968 lok_doc_view_post_command (LOKDocView* pDocView,
2969 const gchar* pCommand,
2970 const gchar* pArguments,
2971 gboolean bNotifyWhenFinished)
2973 LOKDocViewPrivate& priv = getPrivate(pDocView);
2974 if (!priv->m_pDocument)
2975 return;
2977 if (priv->m_bEdit)
2978 LOKPostCommand(pDocView, pCommand, pArguments, bNotifyWhenFinished);
2979 else
2980 g_info ("LOK_POST_COMMAND: ignoring commands in view-only mode");
2983 SAL_DLLPUBLIC_EXPORT void
2984 lok_doc_view_find_prev (LOKDocView* pDocView,
2985 const gchar* pText,
2986 gboolean bHighlightAll)
2988 doSearch(pDocView, pText, true, bHighlightAll);
2991 SAL_DLLPUBLIC_EXPORT void
2992 lok_doc_view_find_next (LOKDocView* pDocView,
2993 const gchar* pText,
2994 gboolean bHighlightAll)
2996 doSearch(pDocView, pText, false, bHighlightAll);
2999 SAL_DLLPUBLIC_EXPORT void
3000 lok_doc_view_highlight_all (LOKDocView* pDocView,
3001 const gchar* pText)
3003 doSearch(pDocView, pText, false, true);
3006 SAL_DLLPUBLIC_EXPORT gchar*
3007 lok_doc_view_copy_selection (LOKDocView* pDocView,
3008 const gchar* pMimeType,
3009 gchar** pUsedMimeType)
3011 LibreOfficeKitDocument* pDocument = lok_doc_view_get_document(pDocView);
3012 if (!pDocument)
3013 return nullptr;
3015 std::stringstream ss;
3016 ss << "lok::Document::getTextSelection('" << pMimeType << "')";
3017 g_info("%s", ss.str().c_str());
3018 return pDocument->pClass->getTextSelection(pDocument, pMimeType, pUsedMimeType);
3021 SAL_DLLPUBLIC_EXPORT gboolean
3022 lok_doc_view_paste (LOKDocView* pDocView,
3023 const gchar* pMimeType,
3024 const gchar* pData,
3025 gsize nSize)
3027 LOKDocViewPrivate& priv = getPrivate(pDocView);
3028 LibreOfficeKitDocument* pDocument = priv->m_pDocument;
3029 gboolean ret = 0;
3031 if (!pDocument)
3032 return false;
3034 if (!priv->m_bEdit)
3036 g_info ("ignoring paste in view-only mode");
3037 return ret;
3040 if (pData)
3042 std::stringstream ss;
3043 ss << "lok::Document::paste('" << pMimeType << "', '" << std::string(pData, nSize) << ", "<<nSize<<"')";
3044 g_info("%s", ss.str().c_str());
3045 ret = pDocument->pClass->paste(pDocument, pMimeType, pData, nSize);
3048 return ret;
3051 SAL_DLLPUBLIC_EXPORT void
3052 lok_doc_view_set_document_password (LOKDocView* pDocView,
3053 const gchar* pURL,
3054 const gchar* pPassword)
3056 LOKDocViewPrivate& priv = getPrivate(pDocView);
3058 priv->m_pOffice->pClass->setDocumentPassword(priv->m_pOffice, pURL, pPassword);
3061 SAL_DLLPUBLIC_EXPORT gfloat
3062 lok_doc_view_pixel_to_twip (LOKDocView* pDocView, float fInput)
3064 LOKDocViewPrivate& priv = getPrivate(pDocView);
3065 return pixelToTwip(fInput, priv->m_fZoom);
3068 SAL_DLLPUBLIC_EXPORT gfloat
3069 lok_doc_view_twip_to_pixel (LOKDocView* pDocView, float fInput)
3071 LOKDocViewPrivate& priv = getPrivate(pDocView);
3072 return twipToPixel(fInput, priv->m_fZoom);
3075 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */