17#include <com/sun/star/frame/Desktop.hpp>
18#include <com/sun/star/ui/ContextChangeEventObject.hpp>
22#include <rtl/strbuf.hxx>
31#include <sfx2/sfxsids.hrc>
33#include <LibreOfficeKit/LibreOfficeKitEnums.h>
37#include <boost/property_tree/json_parser.hpp>
52 assert(m_nDisabled >= 0 &&
"Expected non-negative DisabledCallbacks state when disabling.");
58 assert(m_nDisabled > 0 &&
"Expected positive DisabledCallbacks state when re-enabling.");
62 static inline bool disabled()
68 static int m_nDisabled;
71int DisableCallbacks::m_nDisabled = 0;
79bool g_isDefaultTimezoneSet =
false;
80OUString g_DefaultTimezone;
85 assert(docId >=
ViewShellDocId(0) &&
"Cannot createView for invalid (negative) DocId.");
88 SfxRequest aRequest(rViewFrame, SID_NEWWINDOW);
91 if (pViewShell ==
nullptr)
94 assert(pViewShell->
GetDocId() == docId &&
"DocId must be already set!");
104 if (pViewShell ==
nullptr)
125 if (pViewShell->GetDocId() == docId)
126 return createView(pViewShell->GetViewFrame(), docId);
136 pDoc->setEditMode(nMode);
150 if (pViewShell->GetViewShellId() == nViewShellId)
152 pViewShell->SetLOKAccessibilityState(
false);
154 SfxRequest aRequest(rViewFrame, SID_CLOSEWIN);
172 if (pViewShell->GetViewShellId() == nViewShellId)
189 xDesktop->setActiveFrame(
xFrame);
206 if (pViewShell->GetViewShellId() == nViewShellId)
226 assert(nDocId != -1 &&
"Cannot getViewsCount for invalid DocId -1");
237 if (pViewShell->
GetDocId() == nCurrentDocId)
247 assert(nDocId != -1 &&
"Cannot getViewsIds for invalid DocId -1");
258 if (pViewShell->
GetDocId() == nCurrentDocId)
279 return static_cast<int>(pViewShell->
GetDocId());
287 return g_defaultLanguageTag;
292 g_defaultLanguageTag =
LanguageTag(rBcp47LanguageTag,
true);
299 g_loadLanguageTag =
LanguageTag(rBcp47LanguageTag,
true);
310 pViewShell->SetLOKLanguageTag(rBcp47LanguageTag);
324 pViewShell->SetLOKAccessibilityState(nEnabled);
338 pViewShell->SetLOKLocale(rBcp47LanguageTag);
346 return g_deviceFormFactor;
351 if (rDeviceFormFactor ==
u"desktop")
353 else if (rDeviceFormFactor ==
u"tablet")
355 else if (rDeviceFormFactor ==
u"mobile")
363 g_isDefaultTimezoneSet = isSet;
364 g_DefaultTimezone = rTimezone;
369 return { g_isDefaultTimezoneSet, g_DefaultTimezone };
380 pViewShell->SetLOKTimezone(isSet, rTimezone);
394 return pViewShell->GetLOKTimezone();
407 if (rStr.getLength() < 1)
410 OStringBuffer
aBuf(rStr.getLength() + 8);
411 for (sal_Int32
i = 0;
i < rStr.getLength(); ++
i)
413 if (rStr[
i] ==
'"' || rStr[
i] ==
'\\')
417 aBuf.append(rStr[
i]);
419 return aBuf.makeStringAndClear();
424 assert(pView !=
nullptr &&
"pView must be valid");
425 boost::property_tree::ptree aMessageProps = rTree;
427 aMessageProps.put(
"part", pView->
getPart());
429 std::stringstream aStream;
430 boost::property_tree::write_json(aStream, aMessageProps,
false );
435 const OString& rPayload)
437 assert(pView !=
nullptr &&
"pView must be valid");
438 return OString::Concat(
"{ \"viewId\": \"") + OString::number(nViewId)
439 +
"\", \"part\": \"" + OString::number(pView->
getPart()) +
"\", \"mode\": \""
440 + OString::number(pView->
getEditMode()) +
"\", \"" + rKey +
"\": \""
445 const OString& rPayload)
451 int nType, std::string_view rKey,
const OString& rPayload)
453 assert(pThisView !=
nullptr &&
"pThisView must be valid");
454 if (DisableCallbacks::disabled())
463 int nType,
const boost::property_tree::ptree& rTree)
465 assert(pThisView !=
nullptr &&
"pThisView must be valid");
466 if (DisableCallbacks::disabled())
474 const OString& rPayload)
476 assert(pThisView !=
nullptr &&
"pThisView must be valid");
477 if (DisableCallbacks::disabled())
488 if (pViewShell != pThisView && nCurrentDocId == pViewShell->
GetDocId())
491 if (aPayload.isEmpty())
505 const boost::property_tree::ptree& rTree)
507 assert(pThisView !=
nullptr &&
"pThisView must be valid");
508 if (DisableCallbacks::disabled())
519 if (pViewShell != pThisView && nCurrentDocId == pViewShell->
GetDocId())
522 if (aPayload.isEmpty())
541 OUString lcl_getNameForSlot(
const SfxViewShell* pShell, sal_uInt16 nWhich)
561 if (!pShell || !pItem || pItem ==
INVALID_POOL_ITEM || DisableCallbacks::disabled())
564 boost::property_tree::ptree aItem = pItem->
dumpAsJSON();
566 if (aItem.count(
"state"))
568 OUString sCommand = lcl_getNameForSlot(pShell, pItem->
Which());
569 if (!sCommand.isEmpty())
570 aItem.put(
"commandName", sCommand);
572 std::stringstream aStream;
573 boost::property_tree::write_json(aStream, aItem);
585 std::u16string_view rAction,
586 const std::vector<vcl::LOKPayloadItem>& rPayload)
588 assert(pThisView !=
nullptr &&
"pThisView must be valid");
590 if (nLOKWindowId == 0 || DisableCallbacks::disabled())
593 OStringBuffer aPayload =
594 "{ \"id\": \"" + OString::number(nLOKWindowId) +
"\""
597 for (
const auto& rItem: rPayload)
599 if (!rItem.first.isEmpty() && !rItem.second.isEmpty())
601 auto aFirst = rItem.first.replaceAll(
"\"",
"\\\"");
602 auto aSecond = rItem.second.replaceAll(
"\"",
"\\\"");
603 aPayload.append(
", \"" + aFirst +
"\": \"" + aSecond +
"\"");
606 aPayload.append(
'}');
608 const OString s = aPayload.makeStringAndClear();
614 if (DisableCallbacks::disabled())
625 if (!pDoc || pDoc->isDisposed() || DisableCallbacks::disabled())
630 for (
int i = 0;
i < pDoc->getParts(); ++
i)
642 if (DisableCallbacks::disabled())
653 if (pCurrentViewShell ==
nullptr || pViewShell->
GetDocId() == pCurrentViewShell-> GetDocId())
656 bInvalidateAll =
false;
664 if (DisableCallbacks::disabled())
670 if (pViewShell->
getPart() == nPart)
677 bool bMispelledWord,
const OString& rHyperlink)
681 OString sHyperlink = rHyperlink.isEmpty() ?
"{}" : rHyperlink;
682 return OString::Concat(
"{ \"viewId\": \"") + OString::number(nViewId) +
683 "\", \"rectangle\": \"" + rRectangle +
684 "\", \"mispelledWord\": \"" + OString::number(bMispelledWord ? 1 : 0) +
685 "\", \"hyperlink\": " + sHyperlink +
" }";
695 if (DisableCallbacks::disabled())
698 const auto payload = rPayload.getStr();
700 if (!pCurrentViewShell)
713 if (DisableCallbacks::disabled())
721 rEvent.ApplicationName.replace(
' ',
'_') +
723 rEvent.ContextName.replace(
' ',
'_');
729 if (DisableCallbacks::disabled())
743 if (DisableCallbacks::disabled())
753 assert(pThisView !=
nullptr &&
"pThisView must be valid");
754 if (DisableCallbacks::disabled())
762 if (pViewShell != pThisView && nCurrentDocId == pViewShell->
GetDocId())
771 struct LOKAsyncEventData
781 void LOKPostAsyncEvent(
void* pEv,
void*)
783 std::unique_ptr<LOKAsyncEventData> pLOKEv(
static_cast<LOKAsyncEventData*
>(pEv));
784 if (pLOKEv->mpWindow->isDisposed())
788 if (nView != pLOKEv->mnView)
790 SAL_INFO(
"sfx.view",
"LOK - view mismatch " << nView <<
" vs. " << pLOKEv->mnView);
794 if (!pLOKEv->mpWindow->HasChildPathFocus(
true))
796 SAL_INFO(
"sfx.view",
"LOK - focus mismatch, switching focus");
797 pLOKEv->mpWindow->GrabFocus();
802 pFocusWindow = pLOKEv->mpWindow;
804 if (pLOKEv->mpWindow->isDisposed())
807 switch (pLOKEv->mnEvent)
809 case VclEventId::WindowKeyInput:
811 sal_uInt16 nRepeat = pLOKEv->maKeyEvent.GetRepeat();
812 KeyEvent singlePress(pLOKEv->maKeyEvent.GetCharCode(),
813 pLOKEv->maKeyEvent.GetKeyCode());
814 for (sal_uInt16 i = 0;
i <= nRepeat; ++
i)
815 if (!pFocusWindow->isDisposed())
816 pFocusWindow->KeyInput(singlePress);
819 case VclEventId::WindowKeyUp:
820 if (!pFocusWindow->isDisposed())
821 pFocusWindow->KeyUp(pLOKEv->maKeyEvent);
823 case VclEventId::WindowMouseButtonDown:
824 pLOKEv->mpWindow->SetLastMousePos(pLOKEv->maMouseEvent.GetPosPixel());
825 pLOKEv->mpWindow->MouseButtonDown(pLOKEv->maMouseEvent);
827 if (pLOKEv->maMouseEvent.GetButtons() & MOUSE_RIGHT)
829 const CommandEvent aCEvt(pLOKEv->maMouseEvent.GetPosPixel(), CommandEventId::ContextMenu,
true,
nullptr);
830 pLOKEv->mpWindow->Command(aCEvt);
833 case VclEventId::WindowMouseButtonUp:
834 pLOKEv->mpWindow->SetLastMousePos(pLOKEv->maMouseEvent.GetPosPixel());
835 pLOKEv->mpWindow->MouseButtonUp(pLOKEv->maMouseEvent);
839 if (pLOKEv->mpWindow->IsTracking())
840 pLOKEv->mpWindow->EndTracking();
843 case VclEventId::WindowMouseMove:
844 pLOKEv->mpWindow->SetLastMousePos(pLOKEv->maMouseEvent.GetPosPixel());
845 pLOKEv->mpWindow->MouseMove(pLOKEv->maMouseEvent);
847 case VclEventId::ExtTextInput:
848 case VclEventId::EndExtTextInput:
849 pLOKEv->mpWindow->PostExtTextInputEvent(pLOKEv->mnEvent, pLOKEv->maText);
857 void postEventAsync(LOKAsyncEventData *pEvent)
859 if (!pEvent->mpWindow || pEvent->mpWindow->isDisposed())
861 SAL_WARN(
"vcl",
"Async event post - but no valid window as destination " << pEvent->mpWindow.get());
870 SAL_WARN(
"lok",
"Posting event directly but not called from main thread!");
871 LOKPostAsyncEvent(pEvent,
nullptr);
879 int nType,
int nCharCode,
int nKeyCode,
int nRepeat)
881 LOKAsyncEventData* pLOKEv =
new LOKAsyncEventData;
884 case LOK_KEYEVENT_KEYINPUT:
885 pLOKEv->mnEvent = VclEventId::WindowKeyInput;
887 case LOK_KEYEVENT_KEYUP:
888 pLOKEv->mnEvent = VclEventId::WindowKeyUp;
893 pLOKEv->maKeyEvent =
KeyEvent(nCharCode, nKeyCode, nRepeat);
894 pLOKEv->mpWindow = xWindow;
895 postEventAsync(pLOKEv);
909 int nType,
const OUString &rText)
911 LOKAsyncEventData* pLOKEv =
new LOKAsyncEventData;
914 case LOK_EXT_TEXTINPUT:
915 pLOKEv->mnEvent = VclEventId::ExtTextInput;
916 pLOKEv->maText = rText;
918 case LOK_EXT_TEXTINPUT_END:
919 pLOKEv->mnEvent = VclEventId::EndExtTextInput;
925 pLOKEv->mpWindow = xWindow;
926 postEventAsync(pLOKEv);
931 LOKAsyncEventData* pLOKEv =
new LOKAsyncEventData;
932 switch (rLokMouseEventData.
mnType)
934 case LOK_MOUSEEVENT_MOUSEBUTTONDOWN:
935 pLOKEv->mnEvent = VclEventId::WindowMouseButtonDown;
937 case LOK_MOUSEEVENT_MOUSEBUTTONUP:
938 pLOKEv->mnEvent = VclEventId::WindowMouseButtonUp;
940 case LOK_MOUSEEVENT_MOUSEMOVE:
941 pLOKEv->mnEvent = VclEventId::WindowMouseMove;
948 assert (rLokMouseEventData.
meModifiers == MouseEventModifiers::SIMPLECLICK);
955 pLOKEv->maMouseEvent.setLogicPosition(*rLokMouseEventData.
maLogicPosition);
957 pLOKEv->mpWindow = xWindow;
958 postEventAsync(pLOKEv);
964 sal_Int32 nDocId = pShell ?
static_cast<sal_Int32
>(pShell->
GetDocId().
get()) : -1;
966 rState.append(
"\n\tDocId:\t");
967 rState.append(nDocId);
972 rState.append(
"\n\tViewCount:\t");
973 rState.append(
static_cast<sal_Int32
>(
getViewsCount(nDocId)));
979 if (pCurrentViewShell ==
nullptr || pViewShell->
GetDocId() == pCurrentViewShell-> GetDocId())
987 int nY,
int nCount,
int nButtons,
988 int nModifier,
double fScaleX,
double fScaleY,
1007 if (
nType != LOK_MOUSEEVENT_MOUSEMOVE)
SfxApplication * SfxGetpApp()
static bool IsMainThread()
static ImplSVEvent * PostUserEvent(const Link< void *, void > &rLink, void *pCaller=nullptr, bool bReferenceLink=false)
A class for chart editing support via LibreOfficeKit.
static bool HitAny(const Point &aPos, bool bNegativeX=false)
bool postMouseEvent(int nType, int nX, int nY, int nCount, int nButtons, int nModifier, double fScaleX=1.0, double fScaleY=1.0)
A class for math editing support via LibreOfficeKit.
bool postMouseEvent(int nType, int nX, int nY, int nCount, int nButtons, int nModifier, double fPPTScaleX, double fPPTScaleY)
static SfxApplication * Get()
SAL_DLLPRIVATE std::vector< SfxViewShell * > & GetViewShells_Impl() const
SAL_DLLPRIVATE std::unordered_map< OUString, css::uno::Reference< css::ui::XAcceleratorConfiguration > > & GetAcceleratorConfs_Impl() const
static SfxApplication * GetOrCreate()
const css::uno::Reference< css::frame::XFrame > & GetFrameInterface() const
static std::pair< bool, OUString > getViewTimezone(int nId)
Get the timezone of the given view.
static void notifyInvalidation(SfxViewShell const *pThisView, tools::Rectangle const *)
Emits a LOK_CALLBACK_INVALIDATE_TILES, but tweaks it according to setOptionalFeatures() if needed.
static void setBlockedCommandList(int nViewId, const char *blockedCommandList)
Set View Blocked for some uno commands.
static void setView(int nId)
Set a view shell as current one.
static void notifyAllViews(int nType, const OString &rPayload)
Notifies all views with the given type and payload.
static OString makeVisCursorInvalidation(int nViewId, const OString &rRectangle, bool bMispelledWord=false, const OString &rHyperlink="")
Makes a LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR payload, but tweaks it according to setOptionalFeature...
static std::size_t getViewsCount(int nDocId)
Get the number of views of the current DocId.
static void postKeyEventAsync(const VclPtr< vcl::Window > &xWindow, int nType, int nCharCode, int nKeyCode, int nRepeat=0)
Helper for posting async key event.
static LOKDeviceFormFactor getDeviceFormFactor()
Get the device form factor that should be used for a new view.
static void notifyUpdatePerViewId(SfxViewShell const *pViewShell, int nType)
static int createView()
Create a new view shell from the current view frame.
static void notifyWindow(const SfxViewShell *pThisView, vcl::LOKWindowId nWindowId, std::u16string_view rAction, const std::vector< vcl::LOKPayloadItem > &rPayload=std::vector< vcl::LOKPayloadItem >())
Emits a LOK_CALLBACK_WINDOW.
static void notifyDocumentSizeChangedAllViews(vcl::ITiledRenderable *pDoc, bool bInvalidateAll=true)
Emits a LOK_CALLBACK_DOCUMENT_SIZE_CHANGED for all views of the same document - if @bInvalidateAll - ...
static void setViewTimezone(int nId, bool isSet, const OUString &rTimezone)
Set the timezone of the given view.
static void notifyOtherView(const SfxViewShell *pThisView, SfxViewShell const *pOtherView, int nType, std::string_view rKey, const OString &rPayload)
Same as notifyOtherViews(), but works on a selected "other" view, not on all of them.
static void sendUnoStatus(const SfxViewShell *pShell, const SfxPoolItem *pItem)
Emits a LOK_CALLBACK_STATE_CHANGED.
static void destroyView(int nId)
Destroy a view shell from the global shell list.
static void setDefaultTimezone(bool isSet, const OUString &rTimezone)
Set timezone of the given view.
static bool testInPlaceComponentMouseEventHit(SfxViewShell *pViewShell, int nType, int nX, int nY, int nCount, int nButtons, int nModifier, double fScaleX, double fScaleY, bool bNegativeX=false)
Process the mouse event in the currently active in-place component (if any).
static void setViewLocale(int nId, const OUString &rBcp47LanguageTag)
Set the locale for the given view.
static void notifyUpdate(SfxViewShell const *pViewShell, int nType)
static void notifyPartSizeChangedAllViews(vcl::ITiledRenderable *pDoc, int nPart)
Emits a LOK_CALLBACK_DOCUMENT_SIZE_CHANGED for all views of the same document with the same part.
static void notifyOtherViews(const SfxViewShell *pThisView, int nType, std::string_view rKey, const OString &rPayload)
Invoke the LOK callback of all other views showing the same document as pThisView,...
static void setDeviceFormFactor(std::u16string_view rDeviceFormFactor)
Set the device form factor that should be used for a new view.
static bool getViewIds(int nDocId, int *pArray, size_t nSize)
Get viewIds of views of the current DocId.
static void postExtTextEventAsync(const VclPtr< vcl::Window > &xWindow, int nType, const OUString &rText)
Helper for posting input event.
static const LanguageTag & getDefaultLanguage()
Get the default language that should be used for views.
static void setDefaultLanguage(const OUString &rBcp47LanguageTag)
Set the default language for views.
static OString makePayloadJSON(const SfxViewShell *pThisView, int nViewId, std::string_view rKey, const OString &rPayload)
static VclPtr< vcl::Window > getInPlaceDocWindow(SfxViewShell *pViewShell)
static std::unordered_map< OUString, css::uno::Reference< com::sun::star::ui::XAcceleratorConfiguration > > & getAcceleratorConfs()
Gets the short cut accelerators.
static void notifyOtherViewsUpdatePerViewId(SfxViewShell const *pViewShell, int nType)
static void setLoadLanguage(const OUString &rBcp47LanguageTag)
Set the language used by the loading view (used for all save operations).
static std::pair< bool, OUString > getDefaultTimezone()
Get timezone of the given view. See @setDefaultTimezone.
static SfxViewShell * getViewOfId(int nId)
Get view shell with id.
static void setAccessibilityState(int nId, bool nEnabled)
Enable/Disable AT support for the given view.
static void postMouseEventAsync(const VclPtr< vcl::Window > &xWindow, LokMouseEventData const &rLokMouseEventData)
Helper for posting async mouse event.
static void notifyDocumentSizeChanged(SfxViewShell const *pThisView, const OString &rPayload, vcl::ITiledRenderable *pDoc, bool bInvalidateAll=true)
Emits a LOK_CALLBACK_DOCUMENT_SIZE_CHANGED - if @bInvalidateAll - first invalidates all parts.
static int getView(const SfxViewShell *pViewShell=nullptr)
Get the currently active view.
static void notifyViewRenderState(SfxViewShell const *pViewShell, vcl::ITiledRenderable *pDoc)
Emits an LOK_CALLBACK_VIEW_RENDER_STATE.
static const LanguageTag & getLoadLanguage()
Get the language used by the loading view (used for all save operations).
static void notifyContextChange(const css::ui::ContextChangeEventObject &rEvent)
Notify about the editing context change.
static int getDocumentIdOfView(int nViewId)
Get the document id for a view.
static void setViewLanguage(int nId, const OUString &rBcp47LanguageTag)
Set language of the given view.
static void setEditMode(int nMode, vcl::ITiledRenderable *pDoc)
Set the edit mode for a document with callbacks disabled.
static void dumpState(rtl::OStringBuffer &rState)
Helper for diagnosing run-time problems.
virtual boost::property_tree::ptree dumpAsJSON() const
SfxViewFrame * GetFrame() const
This method returns a pointer to the <SfxViewFrame> to which this SfxShell instance is associated or ...
static SfxSlotPool & GetSlotPool(SfxViewFrame *pFrame=nullptr)
const SfxSlot * GetSlot(sal_uInt16 nId) const
SFX2_DLLPUBLIC OUString GetCommand() const
const OUString & GetUnoName() const
SAL_DLLPRIVATE void Exec_Impl(SfxRequest &)
SfxFrame & GetFrame() const
SAL_DLLPRIVATE void MakeActive_Impl(bool bActivate)
SAL_DLLPRIVATE void ExecView_Impl(SfxRequest &rReq)
One SfxViewShell more or less represents one edit window for a document, there can be multiple ones f...
virtual void libreOfficeKitViewCallback(int nType, const OString &pPayload) const override
Invokes the registered callback, if there are any.
ViewShellDocId GetDocId() const override
Get the DocId used by Mobile LOKit to load multiple documents.
ViewShellId GetViewShellId() const override
See OutlinerViewShell::GetViewShellId().
SfxViewFrame & GetViewFrame() const
static SAL_WARN_UNUSED_RESULT SfxViewShell * Get(const css::uno::Reference< css::frame::XController > &i_rController)
static SAL_WARN_UNUSED_RESULT SfxViewShell * GetNext(const SfxViewShell &rPrev, bool bOnlyVisible=true, const std::function< bool(const SfxViewShell *)> &isViewShell=nullptr)
virtual int getEditMode() const
See lok::Document::getMode().
virtual void libreOfficeKitViewUpdatedCallbackPerViewId(int nType, int nViewId, int nSourceViewId) const override
virtual void libreOfficeKitViewInvalidateTilesCallback(const tools::Rectangle *pRect, int nPart, int nMode) const override
void dumpLibreOfficeKitViewState(rtl::OStringBuffer &rState)
dump view state for diagnostics
virtual void libreOfficeKitViewUpdatedCallback(int nType) const override
static SAL_WARN_UNUSED_RESULT SfxViewShell * GetFirst(bool bOnlyVisible=true, const std::function< bool(const SfxViewShell *)> &isViewShell=nullptr)
static SAL_WARN_UNUSED_RESULT SfxViewShell * Current()
static void SetCurrentDocId(ViewShellDocId nId)
Set the current DocId, which is used by Mobile LOKit to load multiple documents and yet identify the ...
void setBlockedCommandList(const char *blockedCommandList)
virtual void libreOfficeKitViewCallbackWithViewId(int nType, const OString &pPayload, int nViewId) const override
virtual int getPart() const
See lok::Document::getPart().
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
static OString lcl_sanitizeJSONAsValue(const OString &rStr)
static OString lcl_generateJSON(const SfxViewShell *pView, const boost::property_tree::ptree &rTree)
bool isPartInInvalidation()
bool isViewIdForVisCursorInvalidation()
void setLocale(const LanguageTag &languageTag)
void setLanguageTag(const LanguageTag &languageTag)
Reference< XComponentContext > getProcessComponentContext()
std::basic_string_view< charT, traits > trim(std::basic_string_view< charT, traits > str)
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
#define INVALID_POOL_ITEM
std::optional< Point > maLogicPosition
MouseEventModifiers meModifiers
UNDERLYING_TYPE get() const
Reference< XFrame > xFrame
std::unique_ptr< char[]> aBuffer
VclPtr< vcl::Window > mpWindow