summaryrefslogtreecommitdiff
path: root/vcl/osx
diff options
context:
space:
mode:
authorTor Lillqvist <tml@collabora.com>2013-12-05 22:01:05 +0200
committerTor Lillqvist <tml@collabora.com>2013-12-06 15:05:00 +0200
commitc49721950cb3d897b35f08bf871239308680b18e (patch)
tree6a76e91557328b9195960d065065b1a6914bf9be /vcl/osx
parent693eced961a3d3014d15e0a406f4e001ee817522 (diff)
Re-organize OS X and iOS code in vcl a bit
Now with the ATSUI code gone is a good time for some re-organisation. Get rid of "aqua" in file names and the separate "coretext" folders. CoreText is all we use now for OS X (and has always been so for iOS), so no need for a "coretext" folder, we can keep the CoreText-using code under "quartz". Keep OS X -specific code in "osx". Ditto for headers. Keep "Aqua" as part of class names for now, though. This is also preparation for planned further unification between OS X and iOS code. Change-Id: Ic60bd73fea4ab98183e7c8a09c7d3f66b9a34223
Diffstat (limited to 'vcl/osx')
-rw-r--r--vcl/osx/DataFlavorMapping.cxx741
-rw-r--r--vcl/osx/DataFlavorMapping.hxx141
-rw-r--r--vcl/osx/DragActionConversion.cxx86
-rw-r--r--vcl/osx/DragActionConversion.hxx40
-rw-r--r--vcl/osx/DragSource.cxx372
-rw-r--r--vcl/osx/DragSource.hxx135
-rw-r--r--vcl/osx/DragSourceContext.cxx61
-rw-r--r--vcl/osx/DragSourceContext.hxx59
-rw-r--r--vcl/osx/DropTarget.cxx596
-rw-r--r--vcl/osx/DropTarget.hxx163
-rw-r--r--vcl/osx/HtmlFmtFlt.cxx168
-rw-r--r--vcl/osx/HtmlFmtFlt.hxx41
-rw-r--r--vcl/osx/OSXTransferable.cxx203
-rw-r--r--vcl/osx/OSXTransferable.hxx90
-rw-r--r--vcl/osx/PictToBmpFlt.cxx76
-rw-r--r--vcl/osx/PictToBmpFlt.hxx39
-rw-r--r--vcl/osx/README.a11y7
-rw-r--r--vcl/osx/a11yactionwrapper.h35
-rw-r--r--vcl/osx/a11yactionwrapper.mm77
-rw-r--r--vcl/osx/a11ycomponentwrapper.h39
-rw-r--r--vcl/osx/a11ycomponentwrapper.mm102
-rw-r--r--vcl/osx/a11yfactory.mm219
-rw-r--r--vcl/osx/a11yfocuslistener.cxx110
-rw-r--r--vcl/osx/a11yfocuslistener.hxx54
-rw-r--r--vcl/osx/a11yfocustracker.cxx284
-rw-r--r--vcl/osx/a11ylistener.cxx153
-rw-r--r--vcl/osx/a11yrolehelper.h36
-rw-r--r--vcl/osx/a11yrolehelper.mm270
-rw-r--r--vcl/osx/a11yselectionwrapper.h37
-rw-r--r--vcl/osx/a11yselectionwrapper.mm88
-rw-r--r--vcl/osx/a11ytablewrapper.h38
-rw-r--r--vcl/osx/a11ytablewrapper.mm204
-rw-r--r--vcl/osx/a11ytextattributeswrapper.h32
-rw-r--r--vcl/osx/a11ytextattributeswrapper.mm355
-rw-r--r--vcl/osx/a11ytextwrapper.h58
-rw-r--r--vcl/osx/a11ytextwrapper.mm293
-rw-r--r--vcl/osx/a11yutil.h32
-rw-r--r--vcl/osx/a11yutil.mm46
-rw-r--r--vcl/osx/a11yvaluewrapper.h40
-rw-r--r--vcl/osx/a11yvaluewrapper.mm87
-rw-r--r--vcl/osx/a11ywrapper.mm1150
-rw-r--r--vcl/osx/a11ywrapperbutton.h35
-rw-r--r--vcl/osx/a11ywrapperbutton.mm54
-rw-r--r--vcl/osx/a11ywrappercheckbox.h35
-rw-r--r--vcl/osx/a11ywrappercheckbox.mm58
-rw-r--r--vcl/osx/a11ywrappercombobox.h44
-rw-r--r--vcl/osx/a11ywrappercombobox.mm155
-rw-r--r--vcl/osx/a11ywrappergroup.h34
-rw-r--r--vcl/osx/a11ywrappergroup.mm49
-rw-r--r--vcl/osx/a11ywrapperlist.h33
-rw-r--r--vcl/osx/a11ywrapperlist.mm40
-rw-r--r--vcl/osx/a11ywrapperradiobutton.h35
-rw-r--r--vcl/osx/a11ywrapperradiobutton.mm57
-rw-r--r--vcl/osx/a11ywrapperradiogroup.h33
-rw-r--r--vcl/osx/a11ywrapperradiogroup.mm40
-rw-r--r--vcl/osx/a11ywrapperrow.h34
-rw-r--r--vcl/osx/a11ywrapperrow.mm49
-rw-r--r--vcl/osx/a11ywrapperscrollarea.h35
-rw-r--r--vcl/osx/a11ywrapperscrollarea.mm77
-rw-r--r--vcl/osx/a11ywrapperscrollbar.h33
-rw-r--r--vcl/osx/a11ywrapperscrollbar.mm43
-rw-r--r--vcl/osx/a11ywrappersplitter.h33
-rw-r--r--vcl/osx/a11ywrappersplitter.mm40
-rw-r--r--vcl/osx/a11ywrapperstatictext.h34
-rw-r--r--vcl/osx/a11ywrapperstatictext.mm48
-rw-r--r--vcl/osx/a11ywrappertabgroup.h33
-rw-r--r--vcl/osx/a11ywrappertabgroup.mm42
-rw-r--r--vcl/osx/a11ywrappertextarea.h33
-rw-r--r--vcl/osx/a11ywrappertextarea.mm40
-rw-r--r--vcl/osx/a11ywrappertoolbar.h33
-rw-r--r--vcl/osx/a11ywrappertoolbar.mm42
-rw-r--r--vcl/osx/clipboard.cxx372
-rw-r--r--vcl/osx/clipboard.hxx175
-rw-r--r--vcl/osx/documentfocuslistener.cxx241
-rw-r--r--vcl/osx/documentfocuslistener.hxx92
-rw-r--r--vcl/osx/printaccessoryview.mm1381
-rw-r--r--vcl/osx/printview.mm75
-rw-r--r--vcl/osx/res/MainMenu.nib/classes.nib4
-rw-r--r--vcl/osx/res/MainMenu.nib/info.nib21
-rw-r--r--vcl/osx/res/MainMenu.nib/keyedobjects.nibbin0 -> 3615 bytes
-rw-r--r--vcl/osx/res/cursors/airbrush.pngbin0 -> 253 bytes
-rw-r--r--vcl/osx/res/cursors/ase.pngbin0 -> 214 bytes
-rw-r--r--vcl/osx/res/cursors/asn.pngbin0 -> 212 bytes
-rw-r--r--vcl/osx/res/cursors/asne.pngbin0 -> 240 bytes
-rw-r--r--vcl/osx/res/cursors/asns.pngbin0 -> 234 bytes
-rw-r--r--vcl/osx/res/cursors/asnswe.pngbin0 -> 285 bytes
-rw-r--r--vcl/osx/res/cursors/asnw.pngbin0 -> 246 bytes
-rw-r--r--vcl/osx/res/cursors/ass.pngbin0 -> 222 bytes
-rw-r--r--vcl/osx/res/cursors/asse.pngbin0 -> 243 bytes
-rw-r--r--vcl/osx/res/cursors/assw.pngbin0 -> 236 bytes
-rw-r--r--vcl/osx/res/cursors/asw.pngbin0 -> 212 bytes
-rw-r--r--vcl/osx/res/cursors/aswe.pngbin0 -> 228 bytes
-rw-r--r--vcl/osx/res/cursors/chain.pngbin0 -> 344 bytes
-rw-r--r--vcl/osx/res/cursors/chainnot.pngbin0 -> 390 bytes
-rw-r--r--vcl/osx/res/cursors/chart.pngbin0 -> 270 bytes
-rw-r--r--vcl/osx/res/cursors/copydata.pngbin0 -> 336 bytes
-rw-r--r--vcl/osx/res/cursors/copydlnk.pngbin0 -> 340 bytes
-rw-r--r--vcl/osx/res/cursors/copyf.pngbin0 -> 329 bytes
-rw-r--r--vcl/osx/res/cursors/copyf2.pngbin0 -> 344 bytes
-rw-r--r--vcl/osx/res/cursors/copyflnk.pngbin0 -> 339 bytes
-rw-r--r--vcl/osx/res/cursors/crook.pngbin0 -> 291 bytes
-rw-r--r--vcl/osx/res/cursors/crop.pngbin0 -> 239 bytes
-rw-r--r--vcl/osx/res/cursors/darc.pngbin0 -> 172 bytes
-rw-r--r--vcl/osx/res/cursors/dbezier.pngbin0 -> 185 bytes
-rw-r--r--vcl/osx/res/cursors/dcapt.pngbin0 -> 183 bytes
-rw-r--r--vcl/osx/res/cursors/dcirccut.pngbin0 -> 185 bytes
-rw-r--r--vcl/osx/res/cursors/dconnect.pngbin0 -> 183 bytes
-rw-r--r--vcl/osx/res/cursors/dellipse.pngbin0 -> 176 bytes
-rw-r--r--vcl/osx/res/cursors/detectiv.pngbin0 -> 268 bytes
-rw-r--r--vcl/osx/res/cursors/dfree.pngbin0 -> 188 bytes
-rw-r--r--vcl/osx/res/cursors/dline.pngbin0 -> 177 bytes
-rw-r--r--vcl/osx/res/cursors/dpie.pngbin0 -> 183 bytes
-rw-r--r--vcl/osx/res/cursors/dpolygon.pngbin0 -> 191 bytes
-rw-r--r--vcl/osx/res/cursors/drect.pngbin0 -> 172 bytes
-rw-r--r--vcl/osx/res/cursors/dtext.pngbin0 -> 174 bytes
-rw-r--r--vcl/osx/res/cursors/fill.pngbin0 -> 255 bytes
-rw-r--r--vcl/osx/res/cursors/help.pngbin0 -> 303 bytes
-rw-r--r--vcl/osx/res/cursors/hourglass.pngbin0 -> 246 bytes
-rw-r--r--vcl/osx/res/cursors/hshear.pngbin0 -> 223 bytes
-rw-r--r--vcl/osx/res/cursors/linkdata.pngbin0 -> 348 bytes
-rw-r--r--vcl/osx/res/cursors/linkf.pngbin0 -> 336 bytes
-rw-r--r--vcl/osx/res/cursors/magnify.pngbin0 -> 282 bytes
-rw-r--r--vcl/osx/res/cursors/mirror.pngbin0 -> 304 bytes
-rw-r--r--vcl/osx/res/cursors/movebw.pngbin0 -> 320 bytes
-rw-r--r--vcl/osx/res/cursors/movedata.pngbin0 -> 290 bytes
-rw-r--r--vcl/osx/res/cursors/movedlnk.pngbin0 -> 318 bytes
-rw-r--r--vcl/osx/res/cursors/movef.pngbin0 -> 294 bytes
-rw-r--r--vcl/osx/res/cursors/movef2.pngbin0 -> 314 bytes
-rw-r--r--vcl/osx/res/cursors/moveflnk.pngbin0 -> 307 bytes
-rw-r--r--vcl/osx/res/cursors/movept.pngbin0 -> 275 bytes
-rw-r--r--vcl/osx/res/cursors/neswsize.pngbin0 -> 312 bytes
-rw-r--r--vcl/osx/res/cursors/notallow.pngbin0 -> 297 bytes
-rw-r--r--vcl/osx/res/cursors/nullptr.pngbin0 -> 150 bytes
-rw-r--r--vcl/osx/res/cursors/nwsesize.pngbin0 -> 313 bytes
-rw-r--r--vcl/osx/res/cursors/pen.pngbin0 -> 351 bytes
-rw-r--r--vcl/osx/res/cursors/pivotcol.pngbin0 -> 293 bytes
-rw-r--r--vcl/osx/res/cursors/pivotdel.pngbin0 -> 264 bytes
-rw-r--r--vcl/osx/res/cursors/pivotfld.pngbin0 -> 272 bytes
-rw-r--r--vcl/osx/res/cursors/pivotrow.pngbin0 -> 295 bytes
-rw-r--r--vcl/osx/res/cursors/pntbrsh.pngbin0 -> 268 bytes
-rw-r--r--vcl/osx/res/cursors/rotate.pngbin0 -> 274 bytes
-rw-r--r--vcl/osx/res/cursors/tblsele.pngbin0 -> 174 bytes
-rw-r--r--vcl/osx/res/cursors/tblsels.pngbin0 -> 171 bytes
-rw-r--r--vcl/osx/res/cursors/tblselse.pngbin0 -> 183 bytes
-rw-r--r--vcl/osx/res/cursors/tblselsw.pngbin0 -> 183 bytes
-rw-r--r--vcl/osx/res/cursors/tblselw.pngbin0 -> 174 bytes
-rw-r--r--vcl/osx/res/cursors/timemove.pngbin0 -> 249 bytes
-rw-r--r--vcl/osx/res/cursors/timesize.pngbin0 -> 241 bytes
-rw-r--r--vcl/osx/res/cursors/vshear.pngbin0 -> 228 bytes
-rw-r--r--vcl/osx/res/cursors/vtext.pngbin0 -> 162 bytes
-rw-r--r--vcl/osx/saldata.cxx265
-rw-r--r--vcl/osx/salframe.cxx1769
-rw-r--r--vcl/osx/salframeview.mm1805
-rw-r--r--vcl/osx/salinst.cxx1207
-rw-r--r--vcl/osx/salmenu.cxx963
-rw-r--r--vcl/osx/salnativewidgets.cxx1462
-rw-r--r--vcl/osx/salnsmenu.mm207
-rw-r--r--vcl/osx/salnstimer.mm48
-rw-r--r--vcl/osx/salobj.cxx206
-rw-r--r--vcl/osx/salprn.cxx755
-rw-r--r--vcl/osx/salsys.cxx190
-rw-r--r--vcl/osx/saltimer.cxx126
-rw-r--r--vcl/osx/salvd.cxx261
-rw-r--r--vcl/osx/service_entry.cxx68
-rw-r--r--vcl/osx/vclnsapp.mm517
165 files changed, 20418 insertions, 0 deletions
diff --git a/vcl/osx/DataFlavorMapping.cxx b/vcl/osx/DataFlavorMapping.cxx
new file mode 100644
index 000000000000..c5a79b368990
--- /dev/null
+++ b/vcl/osx/DataFlavorMapping.cxx
@@ -0,0 +1,741 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "sal/config.h"
+
+#include <DataFlavorMapping.hxx>
+#include "HtmlFmtFlt.hxx"
+#include "PictToBmpFlt.hxx"
+#include "com/sun/star/datatransfer/UnsupportedFlavorException.hpp"
+#include "com/sun/star/datatransfer/XMimeContentType.hpp"
+#include "com/sun/star/datatransfer/MimeContentTypeFactory.hpp"
+#include "com/sun/star/uno/Sequence.hxx"
+#include "comphelper/processfactory.hxx"
+
+#include <rtl/ustring.hxx>
+#include <osl/endian.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include <premac.h>
+#include <Cocoa/Cocoa.h>
+#include <postmac.h>
+
+using namespace ::com::sun::star::datatransfer;
+using namespace ::com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace cppu;
+using namespace std;
+
+
+namespace // private
+{
+ /* Determine whether or not a DataFlavor is valid.
+ */
+ bool isValidFlavor(const DataFlavor& aFlavor)
+ {
+ size_t len = aFlavor.MimeType.getLength();
+ Type dtype = aFlavor.DataType;
+ return ((len > 0) && ((dtype == getCppuType((Sequence<sal_Int8>*)0)) || (dtype == getCppuType( (OUString*)0 ))));
+ }
+
+ OUString NSStringToOUString(NSString* cfString)
+ {
+ BOOST_ASSERT(cfString && "Invalid parameter");
+
+ const char* utf8Str = [cfString UTF8String];
+ unsigned int len = rtl_str_getLength(utf8Str);
+
+ return OUString(utf8Str, len, RTL_TEXTENCODING_UTF8);
+ }
+
+ NSString* OUStringToNSString(const OUString& ustring)
+ {
+ OString utf8Str = OUStringToOString(ustring, RTL_TEXTENCODING_UTF8);
+ return [NSString stringWithCString: utf8Str.getStr() encoding: NSUTF8StringEncoding];
+ }
+
+ NSString* PBTYPE_SODX = @"application/x-openoffice-objectdescriptor-xml;windows_formatname=\"Star Object Descriptor (XML)\"";
+ NSString* PBTYPE_SESX = @"application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\"";
+ NSString* PBTYPE_SLSDX = @"application/x-openoffice-linksrcdescriptor-xml;windows_formatname=\"Star Link Source Descriptor (XML)\"";
+ NSString* PBTYPE_ESX = @"application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\"";
+ NSString* PBTYPE_LSX = @"application/x-openoffice-link-source-xml;windows_formatname=\"Star Link Source (XML)\"";
+ NSString* PBTYPE_EOX = @"application/x-openoffice-embedded-obj-xml;windows_formatname=\"Star Embedded Object (XML)\"";
+ NSString* PBTYPE_SVXB = @"application/x-openoffice-svbx;windows_formatname=\"SVXB (StarView Bitmap/Animation)\"";
+ NSString* PBTYPE_GDIMF = @"application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"";
+ NSString* PBTYPE_WMF = @"application/x-openoffice-wmf;windows_formatname=\"Image WMF\"";
+ NSString* PBTYPE_EMF = @"application/x-openoffice-emf;windows_formatname=\"Image EMF\"";
+
+ NSString* PBTYPE_DUMMY_INTERNAL = @"application/x-openoffice-internal";
+
+ const char* FLAVOR_SODX = "application/x-openoffice-objectdescriptor-xml;windows_formatname=\"Star Object Descriptor (XML)\"";
+ const char* FLAVOR_SESX = "application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\"";
+ const char* FLAVOR_SLSDX = "application/x-openoffice-linksrcdescriptor-xml;windows_formatname=\"Star Link Source Descriptor (XML)\"";
+ const char* FLAVOR_ESX = "application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\"";
+ const char* FLAVOR_LSX = "application/x-openoffice-link-source-xml;windows_formatname=\"Star Link Source (XML)\"";
+ const char* FLAVOR_EOX = "application/x-openoffice-embedded-obj-xml;windows_formatname=\"Star Embedded Object (XML)\"";
+ const char* FLAVOR_SVXB = "application/x-openoffice-svbx;windows_formatname=\"SVXB (StarView Bitmap/Animation)\"";
+ const char* FLAVOR_GDIMF = "application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"";
+ const char* FLAVOR_WMF = "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"";
+ const char* FLAVOR_EMF = "application/x-openoffice-emf;windows_formatname=\"Image EMF\"";
+
+ const char* FLAVOR_DUMMY_INTERNAL = "application/x-openoffice-internal";
+
+
+ struct FlavorMap
+ {
+ NSString* SystemFlavor;
+ const char* OOoFlavor;
+ const char* HumanPresentableName;
+ bool DataTypeOUString; // sequence<byte> otherwise
+ };
+
+ /* At the moment it appears as if only MS Office pastes "public.html" to the clipboard.
+ */
+ FlavorMap flavorMap[] =
+ {
+ { NSStringPboardType, "text/plain;charset=utf-16", "Unicode Text (UTF-16)", true },
+ { NSRTFPboardType, "text/richtext", "Rich Text Format", false },
+ { NSTIFFPboardType, "image/png", "Portable Network Graphics", false },
+ { NSHTMLPboardType, "text/html", "Plain Html", false },
+ { NSFilenamesPboardType, "application/x-openoffice-filelist;windows_formatname=\"FileList\"", "FileList", false },
+ { PBTYPE_SESX, FLAVOR_SESX, "Star Embed Source (XML)", false },
+ { PBTYPE_SLSDX, FLAVOR_SLSDX, "Star Link Source Descriptor (XML)", false },
+ { PBTYPE_ESX, FLAVOR_ESX, "Star Embed Source (XML)", false },
+ { PBTYPE_LSX, FLAVOR_LSX, "Star Link Source (XML)", false },
+ { PBTYPE_EOX, FLAVOR_EOX, "Star Embedded Object (XML)", false },
+ { PBTYPE_SVXB, FLAVOR_SVXB, "SVXB (StarView Bitmap/Animation", false },
+ { PBTYPE_GDIMF, FLAVOR_GDIMF, "GDIMetaFile", false },
+ { PBTYPE_WMF, FLAVOR_WMF, "Windows MetaFile", false },
+ { PBTYPE_EMF, FLAVOR_EMF, "Windows Enhanced MetaFile", false },
+ { PBTYPE_SODX, FLAVOR_SODX, "Star Object Descriptor (XML)", false },
+ { PBTYPE_DUMMY_INTERNAL, FLAVOR_DUMMY_INTERNAL, "internal data",false }
+ };
+
+
+ #define SIZE_FLAVOR_MAP (sizeof(flavorMap)/sizeof(FlavorMap))
+
+
+ inline bool isByteSequenceType(const Type& theType)
+ {
+ return (theType == getCppuType((Sequence<sal_Int8>*)0));
+ }
+
+ inline bool isOUStringType(const Type& theType)
+ {
+ return (theType == getCppuType( (OUString*)0 ));
+ }
+
+} // namespace private
+
+
+//###########################
+
+/* A base class for other data provider.
+ */
+class DataProviderBaseImpl : public DataProvider
+{
+public:
+ DataProviderBaseImpl(const Any& data);
+ DataProviderBaseImpl(id data);
+ virtual ~DataProviderBaseImpl();
+
+protected:
+ Any mData;
+ //NSData* mSystemData;
+ id mSystemData;
+};
+
+DataProviderBaseImpl::DataProviderBaseImpl(const Any& data) :
+ mData(data),
+ mSystemData(nil)
+{
+}
+
+DataProviderBaseImpl::DataProviderBaseImpl(id data) :
+ mSystemData(data)
+{
+ [mSystemData retain];
+}
+
+
+DataProviderBaseImpl::~DataProviderBaseImpl()
+{
+ if (mSystemData)
+ {
+ [mSystemData release];
+ }
+}
+
+//#################################
+
+class UniDataProvider : public DataProviderBaseImpl
+{
+public:
+ UniDataProvider(const Any& data);
+
+ UniDataProvider(NSData* data);
+
+ virtual NSData* getSystemData();
+
+ virtual Any getOOoData();
+};
+
+UniDataProvider::UniDataProvider(const Any& data) :
+ DataProviderBaseImpl(data)
+{
+}
+
+UniDataProvider::UniDataProvider(NSData* data) :
+ DataProviderBaseImpl(data)
+{
+}
+
+NSData* UniDataProvider::getSystemData()
+{
+ OUString ustr;
+ mData >>= ustr;
+
+ OString strUtf8;
+ ustr.convertToString(&strUtf8, RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS);
+
+ return [NSData dataWithBytes: strUtf8.getStr() length: strUtf8.getLength()];
+}
+
+Any UniDataProvider::getOOoData()
+{
+ Any oOOData;
+
+ if (mSystemData)
+ {
+ oOOData = makeAny(OUString(reinterpret_cast<const sal_Char*>([mSystemData bytes]),
+ [mSystemData length],
+ RTL_TEXTENCODING_UTF8));
+ }
+ else
+ {
+ oOOData = mData;
+ }
+
+ return oOOData;
+}
+
+//###########################
+
+class ByteSequenceDataProvider : public DataProviderBaseImpl
+{
+public:
+ ByteSequenceDataProvider(const Any& data);
+
+ ByteSequenceDataProvider(NSData* data);
+
+ virtual NSData* getSystemData();
+
+ virtual Any getOOoData();
+};
+
+ByteSequenceDataProvider::ByteSequenceDataProvider(const Any& data) :
+ DataProviderBaseImpl(data)
+{
+}
+
+ByteSequenceDataProvider::ByteSequenceDataProvider(NSData* data) :
+ DataProviderBaseImpl(data)
+{
+}
+
+
+NSData* ByteSequenceDataProvider::getSystemData()
+{
+ Sequence<sal_Int8> rawData;
+ mData >>= rawData;
+
+ return [NSData dataWithBytes: rawData.getArray() length: rawData.getLength()];
+}
+
+Any ByteSequenceDataProvider::getOOoData()
+{
+ Any oOOData;
+
+ if (mSystemData)
+ {
+ unsigned int flavorDataLength = [mSystemData length];
+ Sequence<sal_Int8> byteSequence;
+ byteSequence.realloc(flavorDataLength);
+ memcpy(byteSequence.getArray(), [mSystemData bytes], flavorDataLength);
+ oOOData = makeAny(byteSequence);
+ }
+ else
+ {
+ oOOData = mData;
+ }
+
+ return oOOData;
+}
+
+
+//###########################
+
+class HTMLFormatDataProvider : public DataProviderBaseImpl
+{
+public:
+ HTMLFormatDataProvider(const Any& data);
+
+ HTMLFormatDataProvider(NSData* data);
+
+ virtual NSData* getSystemData();
+
+ virtual Any getOOoData();
+};
+
+HTMLFormatDataProvider::HTMLFormatDataProvider(const Any& data) :
+ DataProviderBaseImpl(data)
+{
+}
+
+HTMLFormatDataProvider::HTMLFormatDataProvider(NSData* data) :
+ DataProviderBaseImpl(data)
+{
+}
+
+NSData* HTMLFormatDataProvider::getSystemData()
+{
+ Sequence<sal_Int8> textHtmlData;
+ mData >>= textHtmlData;
+
+ Sequence<sal_Int8> htmlFormatData = TextHtmlToHTMLFormat(textHtmlData);
+
+ return [NSData dataWithBytes: htmlFormatData.getArray() length: htmlFormatData.getLength()];
+}
+
+Any HTMLFormatDataProvider::getOOoData()
+{
+ Any oOOData;
+
+ if (mSystemData)
+ {
+ unsigned int flavorDataLength = [mSystemData length];
+ Sequence<sal_Int8> unkHtmlData;
+
+ unkHtmlData.realloc(flavorDataLength);
+ memcpy(unkHtmlData.getArray(), [mSystemData bytes], flavorDataLength);
+
+ Sequence<sal_Int8>* pPlainHtml = &unkHtmlData;
+ Sequence<sal_Int8> plainHtml;
+
+ if (isHTMLFormat(unkHtmlData))
+ {
+ plainHtml = HTMLFormatToTextHtml(unkHtmlData);
+ pPlainHtml = &plainHtml;
+ }
+
+ oOOData = makeAny(*pPlainHtml);
+ }
+ else
+ {
+ oOOData = mData;
+ }
+
+ return oOOData;
+}
+
+//###########################
+
+class PNGDataProvider : public DataProviderBaseImpl
+{
+ NSBitmapImageFileType meImageType;
+public:
+ PNGDataProvider( const Any&, NSBitmapImageFileType);
+
+ PNGDataProvider( NSData*, NSBitmapImageFileType);
+
+ virtual NSData* getSystemData();
+
+ virtual Any getOOoData();
+};
+
+PNGDataProvider::PNGDataProvider( const Any& data, NSBitmapImageFileType eImageType) :
+ DataProviderBaseImpl(data),
+ meImageType( eImageType )
+{
+}
+
+PNGDataProvider::PNGDataProvider( NSData* data, NSBitmapImageFileType eImageType) :
+ DataProviderBaseImpl(data),
+ meImageType( eImageType )
+{
+}
+
+NSData* PNGDataProvider::getSystemData()
+{
+ Sequence<sal_Int8> pngData;
+ mData >>= pngData;
+
+ Sequence<sal_Int8> imgData;
+ NSData* sysData = NULL;
+ if( PNGToImage( pngData, imgData, meImageType))
+ sysData = [NSData dataWithBytes: imgData.getArray() length: imgData.getLength()];
+
+ return sysData;
+}
+
+/* The AOO 'PCT' filter is not yet good enough to be used
+ and there is no flavor defined for exchanging 'PCT' with AOO
+ so we convert 'PCT' to a PNG and provide this to AOO
+*/
+Any PNGDataProvider::getOOoData()
+{
+ Any oOOData;
+
+ if( mSystemData)
+ {
+ const unsigned int flavorDataLength = [mSystemData length];
+ Sequence<sal_Int8> imgData( flavorDataLength);
+ memcpy( imgData.getArray(), [mSystemData bytes], flavorDataLength);
+
+ Sequence<sal_Int8> pngData;
+ if( ImageToPNG( imgData, pngData, meImageType))
+ oOOData = makeAny( pngData);
+ }
+ else
+ {
+ oOOData = mData;
+ }
+
+ return oOOData;
+}
+
+//######################
+
+class FileListDataProvider : public DataProviderBaseImpl
+{
+public:
+ FileListDataProvider(const Any& data);
+ FileListDataProvider(NSArray* data);
+
+ virtual NSData* getSystemData();
+ virtual Any getOOoData();
+};
+
+FileListDataProvider::FileListDataProvider(const Any& data) :
+ DataProviderBaseImpl(data)
+{
+}
+
+FileListDataProvider::FileListDataProvider(NSArray* data) :
+ DataProviderBaseImpl(data)
+{
+}
+
+NSData* FileListDataProvider::getSystemData()
+{
+ return [NSData data];
+}
+
+Any FileListDataProvider::getOOoData()
+{
+ Any oOOData;
+
+ if (mSystemData)
+ {
+ size_t length = [mSystemData count];
+ size_t lenSeqRequired = 0;
+
+ for (size_t i = 0; i < length; i++)
+ {
+ NSString* fname = [mSystemData objectAtIndex: i];
+ lenSeqRequired += [fname maximumLengthOfBytesUsingEncoding: NSUnicodeStringEncoding] + sizeof(unichar);
+ }
+
+ Sequence<sal_Int8> oOOFileList(lenSeqRequired);
+ unichar* pBuffer = reinterpret_cast<unichar*>(oOOFileList.getArray());
+ memset(pBuffer, 0, lenSeqRequired);
+
+ for (size_t i = 0; i < length; i++)
+ {
+ NSString* fname = [mSystemData objectAtIndex: i];
+ [fname getCharacters: pBuffer];
+ size_t l = [fname length];
+ pBuffer += l + 1;
+ }
+
+ oOOData = makeAny(oOOFileList);
+ }
+ else
+ {
+ oOOData = mData;
+ }
+
+ return oOOData;
+}
+
+//###########################
+
+DataFlavorMapper::DataFlavorMapper()
+{
+ Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
+ mrXMimeCntFactory = MimeContentTypeFactory::create( xContext );
+}
+
+DataFlavorMapper::~DataFlavorMapper()
+{
+ // release potential NSStrings
+ for( OfficeOnlyTypes::iterator it = maOfficeOnlyTypes.begin(); it != maOfficeOnlyTypes.end(); ++it )
+ {
+ [it->second release];
+ it->second = nil;
+ }
+}
+
+DataFlavor DataFlavorMapper::systemToOpenOfficeFlavor(NSString* systemDataFlavor) const
+{
+ DataFlavor oOOFlavor;
+
+ for (size_t i = 0; i < SIZE_FLAVOR_MAP; i++)
+ {
+ if ([systemDataFlavor caseInsensitiveCompare: flavorMap[i].SystemFlavor] == NSOrderedSame)
+ {
+ oOOFlavor.MimeType = OUString::createFromAscii(flavorMap[i].OOoFlavor);
+ oOOFlavor.HumanPresentableName = OUString::createFromAscii(flavorMap[i].HumanPresentableName);
+ oOOFlavor.DataType = flavorMap[i].DataTypeOUString ? getCppuType( (OUString*)0 ) : getCppuType((Sequence<sal_Int8>*)0);
+ return oOOFlavor;
+ }
+ } // for
+
+ // look if this might be an internal type; if it comes in here it must have
+ // been through openOfficeToSystemFlavor before, so it should then be in the map
+ OUString aTryFlavor( NSStringToOUString( systemDataFlavor ) );
+ if( maOfficeOnlyTypes.find( aTryFlavor ) != maOfficeOnlyTypes.end() )
+ {
+ oOOFlavor.MimeType = aTryFlavor;
+ oOOFlavor.HumanPresentableName = OUString();
+ oOOFlavor.DataType = getCppuType((Sequence<sal_Int8>*)0);
+ }
+
+ return oOOFlavor;
+}
+
+NSString* DataFlavorMapper::openOfficeToSystemFlavor(const DataFlavor& oOOFlavor, bool& rbInternal) const
+{
+ NSString* sysFlavor = NULL;
+ rbInternal = false;
+
+ for( size_t i = 0; i < SIZE_FLAVOR_MAP; ++i )
+ {
+ if (oOOFlavor.MimeType.startsWith(OUString::createFromAscii(flavorMap[i].OOoFlavor)))
+ {
+ sysFlavor = flavorMap[i].SystemFlavor;
+ }
+ }
+
+ if(!sysFlavor)
+ {
+ rbInternal = true;
+ OfficeOnlyTypes::const_iterator it = maOfficeOnlyTypes.find( oOOFlavor.MimeType );
+
+ if( it == maOfficeOnlyTypes.end() )
+ sysFlavor = maOfficeOnlyTypes[ oOOFlavor.MimeType ] = OUStringToNSString( oOOFlavor.MimeType );
+ else
+ sysFlavor = it->second;
+ }
+
+ return sysFlavor;
+}
+
+NSString* DataFlavorMapper::openOfficeImageToSystemFlavor(NSPasteboard* pPasteboard) const
+{
+ NSArray *supportedTypes = [NSArray arrayWithObjects: NSTIFFPboardType, nil];
+ NSString *sysFlavor = [pPasteboard availableTypeFromArray:supportedTypes];
+ return sysFlavor;
+}
+
+DataProviderPtr_t DataFlavorMapper::getDataProvider(NSString* systemFlavor, Reference<XTransferable> rTransferable) const
+{
+ DataProviderPtr_t dp;
+
+ try
+ {
+ DataFlavor oOOFlavor = systemToOpenOfficeFlavor(systemFlavor);
+
+ Any data = rTransferable->getTransferData(oOOFlavor);
+
+ if (isByteSequenceType(data.getValueType()))
+ {
+ /*
+ the HTMLFormatDataProvider prepends segment information to HTML
+ this is useful for exchange with MS Word (which brings this stuff from Windows)
+ but annoying for other applications. Since this extension is not a standard datatype
+ on the Mac, let us not provide but provide normal HTML
+
+ if ([systemFlavor caseInsensitiveCompare: NSHTMLPboardType] == NSOrderedSame)
+ {
+ dp = DataProviderPtr_t(new HTMLFormatDataProvider(data));
+ }
+ else
+ */
+ if ([systemFlavor caseInsensitiveCompare: NSTIFFPboardType] == NSOrderedSame)
+ {
+ dp = DataProviderPtr_t( new PNGDataProvider( data, NSTIFFFileType));
+ }
+ else if ([systemFlavor caseInsensitiveCompare: NSFilenamesPboardType] == NSOrderedSame)
+ {
+ dp = DataProviderPtr_t(new FileListDataProvider(data));
+ }
+ else
+ {
+ dp = DataProviderPtr_t(new ByteSequenceDataProvider(data));
+ }
+ }
+ else // Must be OUString type
+ {
+ SAL_WARN_IF(
+ !isOUStringType(data.getValueType()), "vcl",
+ "must be OUString type");
+ dp = DataProviderPtr_t(new UniDataProvider(data));
+ }
+ }
+ catch(UnsupportedFlavorException&)
+ {
+ // Somebody violates the contract of the clipboard
+ // interface @see XTransferable
+ }
+
+ return dp;
+}
+
+DataProviderPtr_t DataFlavorMapper::getDataProvider(const NSString* /*systemFlavor*/, NSArray* systemData) const
+{
+ return DataProviderPtr_t(new FileListDataProvider(systemData));
+}
+
+DataProviderPtr_t DataFlavorMapper::getDataProvider(const NSString* systemFlavor, NSData* systemData) const
+{
+ DataProviderPtr_t dp;
+
+ if ([systemFlavor caseInsensitiveCompare: NSStringPboardType] == NSOrderedSame)
+ {
+ dp = DataProviderPtr_t(new UniDataProvider(systemData));
+ }
+ else if ([systemFlavor caseInsensitiveCompare: NSHTMLPboardType] == NSOrderedSame)
+ {
+ dp = DataProviderPtr_t(new HTMLFormatDataProvider(systemData));
+ }
+ else if ([systemFlavor caseInsensitiveCompare: NSTIFFPboardType] == NSOrderedSame)
+ {
+ dp = DataProviderPtr_t( new PNGDataProvider(systemData, NSTIFFFileType));
+ }
+ else if ([systemFlavor caseInsensitiveCompare: NSFilenamesPboardType] == NSOrderedSame)
+ {
+ //dp = DataProviderPtr_t(new FileListDataProvider(systemData));
+ }
+ else
+ {
+ dp = DataProviderPtr_t(new ByteSequenceDataProvider(systemData));
+ }
+
+ return dp;
+}
+
+bool DataFlavorMapper::isValidMimeContentType(const OUString& contentType) const
+{
+ bool result = true;
+
+ try
+ {
+ Reference<XMimeContentType> xCntType(mrXMimeCntFactory->createMimeContentType(contentType));
+ }
+ catch( IllegalArgumentException& )
+ {
+ result = false;
+ }
+
+ return result;
+}
+
+NSArray* DataFlavorMapper::flavorSequenceToTypesArray(const com::sun::star::uno::Sequence<com::sun::star::datatransfer::DataFlavor>& flavors) const
+{
+ sal_uInt32 nFlavors = flavors.getLength();
+ NSMutableArray* array = [[NSMutableArray alloc] initWithCapacity: 1];
+
+ bool bNeedDummyInternalFlavor(false);
+
+ for (sal_uInt32 i = 0; i < nFlavors; i++)
+ {
+ if( flavors[i].MimeType.startsWith("image/bmp") )
+ {
+ [array addObject: NSTIFFPboardType];
+ }
+ else
+ {
+ NSString* str = openOfficeToSystemFlavor(flavors[i], bNeedDummyInternalFlavor);
+
+ if (str != NULL)
+ {
+ [str retain];
+ [array addObject: str];
+ }
+ }
+ }
+
+ // #i89462# #i90747#
+ // in case no system flavor was found to report
+ // report at least one so D&D between OOo targets works
+ if( [array count] == 0 || bNeedDummyInternalFlavor)
+ {
+ [array addObject: PBTYPE_DUMMY_INTERNAL];
+ }
+
+ return [array autorelease];
+}
+
+com::sun::star::uno::Sequence<com::sun::star::datatransfer::DataFlavor> DataFlavorMapper::typesArrayToFlavorSequence(NSArray* types) const
+{
+ int nFormats = [types count];
+ Sequence<DataFlavor> flavors;
+
+ for (int i = 0; i < nFormats; i++)
+ {
+ NSString* sysFormat = [types objectAtIndex: i];
+ DataFlavor oOOFlavor = systemToOpenOfficeFlavor(sysFormat);
+
+ if (isValidFlavor(oOOFlavor))
+ {
+ flavors.realloc(flavors.getLength() + 1);
+ flavors[flavors.getLength() - 1] = oOOFlavor;
+ }
+ }
+
+ return flavors;
+}
+
+
+NSArray* DataFlavorMapper::getAllSupportedPboardTypes() const
+{
+ NSMutableArray* array = [[NSMutableArray alloc] initWithCapacity: SIZE_FLAVOR_MAP];
+
+ for (sal_uInt32 i = 0; i < SIZE_FLAVOR_MAP; i++)
+ {
+ [array addObject: flavorMap[i].SystemFlavor];
+ }
+
+ return [array autorelease];
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/DataFlavorMapping.hxx b/vcl/osx/DataFlavorMapping.hxx
new file mode 100644
index 000000000000..70f7736b248e
--- /dev/null
+++ b/vcl/osx/DataFlavorMapping.hxx
@@ -0,0 +1,141 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_DATAFLAVORMAPPING_HXX
+#define INCLUDED_VCL_OSX_DATAFLAVORMAPPING_HXX
+
+#include <com/sun/star/datatransfer/DataFlavor.hpp>
+#include <com/sun/star/datatransfer/XMimeContentTypeFactory.hpp>
+#include <com/sun/star/datatransfer/XTransferable.hpp>
+#include <com/sun/star/lang/XMultiComponentFactory.hpp>
+
+#include <premac.h>
+#import <Cocoa/Cocoa.h>
+#include <postmac.h>
+
+#include <boost/unordered_map.hpp>
+#include <memory>
+#include <boost/shared_ptr.hpp>
+
+
+/* An interface to get the clipboard data in either
+ system or OOo format.
+ */
+class DataProvider
+{
+public:
+ virtual ~DataProvider() {};
+
+ /* Get the clipboard data in the system format.
+ The caller has to retain/release the returned
+ CFDataRef on demand.
+ */
+ virtual NSData* getSystemData() = 0;
+
+ /* Get the clipboard data in OOo format.
+ */
+ virtual com::sun::star::uno::Any getOOoData() = 0;
+};
+
+typedef std::auto_ptr<DataProvider> DataProviderPtr_t;
+
+
+//################################
+
+
+class DataFlavorMapper
+{
+public:
+ /* Initialialize a DataFavorMapper instance. Throws a RuntimeException in case the XMimeContentTypeFactory service
+ cannot be created.
+ */
+ DataFlavorMapper();
+ ~DataFlavorMapper();
+
+
+ /* Map a system data flavor to an OpenOffice data flavor.
+ Return an empty string if there is not suiteable
+ mapping from a system data flavor to a OpenOffice data
+ flavor.
+ */
+ com::sun::star::datatransfer::DataFlavor systemToOpenOfficeFlavor(NSString* systemDataFlavor) const;
+
+
+ /* Map an OpenOffice data flavor to a system data flavor.
+ If there is no suiteable mapping available NULL will
+ be returned.
+ */
+ NSString* openOfficeToSystemFlavor(const com::sun::star::datatransfer::DataFlavor& oooDataFlavor, bool& rbInternal) const;
+
+ /* Select the best available image data type
+ If there is no suiteable mapping available NULL will
+ be returned.
+ */
+ NSString* openOfficeImageToSystemFlavor(NSPasteboard* pPasteboard) const;
+
+ /* Get a data provider which is able to provide the data 'rTransferable' offers in a format that can
+ be put on to the system clipboard.
+ */
+ DataProviderPtr_t getDataProvider(NSString* systemFlavor,
+ const com::sun::star::uno::Reference< com::sun::star::datatransfer::XTransferable > rTransferable) const;
+
+
+
+ /* Get a data provider which is able to provide 'systemData' in the OOo expected format.
+ */
+ DataProviderPtr_t getDataProvider(const NSString* systemFlavor, NSArray* systemData) const;
+
+
+ /* Get a data provider which is able to provide 'systemData' in the OOo expected format.
+ */
+ DataProviderPtr_t getDataProvider(const NSString* systemFlavor, NSData* systemData) const;
+
+
+ /* Translate a sequence of DataFlavors into a NSArray of system types.
+ Only those DataFlavors for which a suitable mapping to a system
+ type exist will be contained in the returned types array.
+ */
+ NSArray* flavorSequenceToTypesArray(const com::sun::star::uno::Sequence<com::sun::star::datatransfer::DataFlavor>& flavors) const;
+
+ /* Translate a NSArray of system types into a sequence of DataFlavors.
+ Only those types for which a suitable mapping to a DataFlavor
+ exist will be contained in the new DataFlavor Sequence.
+ */
+ com::sun::star::uno::Sequence<com::sun::star::datatransfer::DataFlavor> typesArrayToFlavorSequence(NSArray* types) const;
+
+ /* Returns an NSArray containing all pasteboard types supported by OOo
+ */
+ NSArray* getAllSupportedPboardTypes() const;
+
+private:
+ /* Determines if the provided Mime content type is valid.
+ */
+ bool isValidMimeContentType(const OUString& contentType) const;
+
+private:
+ ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XMimeContentTypeFactory> mrXMimeCntFactory;
+ typedef boost::unordered_map< OUString, NSString*, OUStringHash > OfficeOnlyTypes;
+ mutable OfficeOnlyTypes maOfficeOnlyTypes;
+};
+
+typedef boost::shared_ptr<DataFlavorMapper> DataFlavorMapperPtr_t;
+
+#endif // INCLUDED_VCL_OSX_DATAFLAVORMAPPING_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/DragActionConversion.cxx b/vcl/osx/DragActionConversion.cxx
new file mode 100644
index 000000000000..d4bb9e5c3c86
--- /dev/null
+++ b/vcl/osx/DragActionConversion.cxx
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "DragActionConversion.hxx"
+#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
+
+
+using namespace com::sun::star::datatransfer::dnd;
+
+
+/* Convert office drag actions as defined in
+ <type>com::sun::star::datatransfer::dnd::DNDConstants</type>
+ into system conform drag actions.
+ */
+unsigned int OfficeToSystemDragActions(sal_Int8 dragActions)
+{
+ unsigned int actions = NSDragOperationNone;
+
+ if (dragActions & DNDConstants::ACTION_COPY)
+ {
+ actions |= NSDragOperationCopy;
+ }
+
+ if (dragActions & DNDConstants::ACTION_MOVE)
+ {
+ actions |= NSDragOperationMove;
+ }
+
+ if (dragActions & DNDConstants::ACTION_LINK)
+ {
+ actions |= NSDragOperationLink;
+ }
+
+ return actions;
+}
+
+/* Convert system conform drag actions into office conform
+ drag actions as defined in
+ <type>com::sun::star::datatransfer::dnd::DNDConstants</type>.
+ */
+sal_Int8 SystemToOfficeDragActions(unsigned int dragActions)
+{
+ sal_Int8 actions = DNDConstants::ACTION_NONE;
+
+ if (dragActions & NSDragOperationCopy)
+ {
+ actions |= DNDConstants::ACTION_COPY;
+ }
+
+ if (dragActions & NSDragOperationMove)
+ {
+ actions |= DNDConstants::ACTION_MOVE;
+ }
+
+ if (dragActions & NSDragOperationLink)
+ {
+ actions |= DNDConstants::ACTION_LINK;
+ }
+
+ // We map NSDragOperationGeneric to ACTION_DEFAULT to
+ // signal that we have to decide for a drag action
+ if (dragActions & NSDragOperationGeneric)
+ {
+ actions |= DNDConstants::ACTION_DEFAULT;
+ }
+
+ return actions;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/DragActionConversion.hxx b/vcl/osx/DragActionConversion.hxx
new file mode 100644
index 000000000000..8c9b057bff0f
--- /dev/null
+++ b/vcl/osx/DragActionConversion.hxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <sal/types.h>
+
+#include <premac.h>
+#import <Cocoa/Cocoa.h>
+#include <postmac.h>
+
+
+/* Convert office drag actions as defined in
+ <type>com::sun::star::datatransfer::dnd::DNDConstants</type>
+ into system conform drag actions.
+ */
+unsigned int OfficeToSystemDragActions(sal_Int8 dragActions);
+
+/* Convert system conform drag actions into office conform
+ drag actions as defined in
+ <type>com::sun::star::datatransfer::dnd::DNDConstants</type>.
+ */
+sal_Int8 SystemToOfficeDragActions(unsigned int dragActions);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/DragSource.cxx b/vcl/osx/DragSource.cxx
new file mode 100644
index 000000000000..720292854707
--- /dev/null
+++ b/vcl/osx/DragSource.cxx
@@ -0,0 +1,372 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
+#include <com/sun/star/datatransfer/XTransferable.hpp>
+#include <com/sun/star/awt/MouseButton.hpp>
+
+#include "rtl/ustring.hxx"
+
+#include "comphelper/makesequence.hxx"
+
+#include "DragSource.hxx"
+#include "DragSourceContext.hxx"
+#include "clipboard.hxx"
+#include "DragActionConversion.hxx"
+
+#include "osx/salframe.h"
+
+#include <memory>
+
+
+using namespace cppu;
+using namespace osl;
+using namespace com::sun::star;
+using namespace com::sun::star::datatransfer;
+using namespace com::sun::star::datatransfer::clipboard;
+using namespace com::sun::star::datatransfer::dnd;
+using namespace com::sun::star::datatransfer::dnd::DNDConstants;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::awt::MouseButton;
+using namespace com::sun::star::awt;
+using namespace com::sun::star::lang;
+using namespace comphelper;
+using namespace std;
+
+
+// For OOo internal D&D we provide the Transferable without NSDragPboard
+// interference as a shortcut
+uno::Reference<XTransferable> DragSource::g_XTransferable;
+NSView* DragSource::g_DragSourceView = nil;
+bool DragSource::g_DropSuccessSet = false;
+bool DragSource::g_DropSuccess = false;
+
+
+OUString dragSource_getImplementationName()
+{
+ return OUString("com.sun.star.comp.datatransfer.dnd.OleDragSource_V1");
+}
+
+Sequence<OUString> dragSource_getSupportedServiceNames()
+{
+ return makeSequence(OUString("com.sun.star.datatransfer.dnd.OleDragSource"));
+}
+
+
+@implementation DragSourceHelper;
+
+-(DragSourceHelper*)initWithDragSource: (DragSource*) pds
+{
+ self = [super init];
+
+ if (self)
+ {
+ mDragSource = pds;
+ }
+
+ return self;
+}
+
+
+-(void)mouseDown: (NSEvent*)theEvent
+{
+ mDragSource->saveMouseEvent(theEvent);
+}
+
+
+-(void)mouseDragged: (NSEvent*)theEvent
+{
+ mDragSource->saveMouseEvent(theEvent);
+}
+
+
+-(unsigned int)draggingSourceOperationMaskForLocal: (BOOL)isLocal
+{
+ return mDragSource->getSupportedDragOperations(isLocal);
+}
+
+
+-(void)draggedImage:(NSImage*)anImage beganAt:(NSPoint)aPoint
+{
+ (void)anImage;
+ (void)aPoint;
+ DragSourceDragEvent dsde(static_cast<OWeakObject*>(mDragSource),
+ new DragSourceContext,
+ mDragSource,
+ DNDConstants::ACTION_COPY,
+ DNDConstants::ACTION_COPY);
+
+ mDragSource->mXDragSrcListener->dragEnter(dsde);
+}
+
+
+-(void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
+{
+ (void)anImage;
+ (void)aPoint;
+ // an internal drop can accept the drop but fail with dropComplete( false )
+ // this is different than the Cocoa API
+ bool bDropSuccess = operation != NSDragOperationNone;
+ if( DragSource::g_DropSuccessSet )
+ bDropSuccess = DragSource::g_DropSuccess;
+
+ DragSourceDropEvent dsde(static_cast<OWeakObject*>(mDragSource),
+ new DragSourceContext,
+ static_cast< XDragSource* >(mDragSource),
+ SystemToOfficeDragActions(operation),
+ bDropSuccess );
+
+ mDragSource->mXDragSrcListener->dragDropEnd(dsde);
+ mDragSource->mXDragSrcListener = uno::Reference<XDragSourceListener>();
+}
+
+
+-(void)draggedImage:(NSImage *)draggedImage movedTo:(NSPoint)screenPoint
+{
+ (void)draggedImage;
+ (void)screenPoint;
+ DragSourceDragEvent dsde(static_cast<OWeakObject*>(mDragSource),
+ new DragSourceContext,
+ mDragSource,
+ DNDConstants::ACTION_COPY,
+ DNDConstants::ACTION_COPY);
+
+ mDragSource->mXDragSrcListener->dragOver(dsde);
+}
+
+@end
+
+
+DragSource::DragSource():
+ WeakComponentImplHelper3<XDragSource, XInitialization, XServiceInfo>(m_aMutex),
+ mView(NULL),
+ mpFrame(NULL),
+ mLastMouseEventBeforeStartDrag(nil),
+ m_MouseButton(0)
+{
+}
+
+
+DragSource::~DragSource()
+{
+ if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
+ [(id <MouseEventListener>)mView unregisterMouseEventListener: mDragSourceHelper];
+ [mDragSourceHelper release];
+}
+
+
+void SAL_CALL DragSource::initialize(const Sequence< Any >& aArguments)
+ throw(Exception)
+{
+ if (aArguments.getLength() < 2)
+ {
+ throw Exception("DragSource::initialize: Not enough parameter.",
+ static_cast<OWeakObject*>(this));
+ }
+
+ Any pNSView = aArguments[1];
+ sal_uInt64 tmp = 0;
+ pNSView >>= tmp;
+ mView = (NSView*)tmp;
+
+ /* All SalFrameView the base class for all VCL system views inherits from
+ NSView in order to get mouse and other events. This is the only way to
+ get these events. In order to start a drag operation we need to provide
+ the mouse event which was the trigger. SalFrameView therefor implements
+ a hook mechanism so that we can get mouse events for our purpose.
+ */
+ if (![mView respondsToSelector: @selector(registerMouseEventListener:)] ||
+ ![mView respondsToSelector: @selector(unregisterMouseEventListener:)])
+ {
+ throw Exception("DragSource::initialize: Provided view doesn't support mouse listener",
+ static_cast<OWeakObject*>(this));
+ }
+ NSWindow* pWin = [mView window];
+ if( ! pWin || ![pWin respondsToSelector: @selector(getSalFrame)] )
+ {
+ throw Exception("DragSource::initialize: Provided view is not attached to a vcl frame",
+ static_cast<OWeakObject*>(this));
+ }
+ mpFrame = (AquaSalFrame*)[pWin performSelector: @selector(getSalFrame)];
+
+ mDragSourceHelper = [[DragSourceHelper alloc] initWithDragSource: this];
+
+ if (mDragSourceHelper == nil)
+ {
+ throw Exception("DragSource::initialize: Cannot initialize DragSource",
+ static_cast<OWeakObject*>(this));
+ }
+
+ [(id <MouseEventListener>)mView registerMouseEventListener: mDragSourceHelper];
+}
+
+
+//----------------------------------------------------
+// XDragSource
+//----------------------------------------------------
+
+sal_Bool SAL_CALL DragSource::isDragImageSupported( )
+ throw(RuntimeException)
+{
+ return true;
+}
+
+
+sal_Int32 SAL_CALL DragSource::getDefaultCursor( sal_Int8 /*dragAction*/ )
+ throw( IllegalArgumentException, RuntimeException)
+{
+ return 0;
+}
+
+
+void SAL_CALL DragSource::startDrag(const DragGestureEvent& trigger,
+ sal_Int8 sourceActions,
+ sal_Int32 /*cursor*/,
+ sal_Int32 /*image*/,
+ const uno::Reference<XTransferable >& transferable,
+ const uno::Reference<XDragSourceListener >& listener )
+ throw( RuntimeException)
+{
+ MutexGuard guard(m_aMutex);
+
+ OSL_ASSERT(listener.is() && "DragSource::startDrag: No XDragSourceListener provided\n");
+ OSL_ASSERT(transferable.is() && "DragSource::startDrag: No transferable provided\n");
+
+ trigger.Event >>= mMouseEvent;
+ m_MouseButton= mMouseEvent.Buttons;
+ mXDragSrcListener = listener;
+ mXCurrentContext = static_cast<XDragSourceContext*>(new DragSourceContext);
+ auto_ptr<AquaClipboard> clipb(new AquaClipboard(NULL, false));
+ g_XTransferable = transferable;
+ clipb->setContents(g_XTransferable, uno::Reference<XClipboardOwner>());
+ mDragSourceActions = sourceActions;
+ g_DragSourceView = mView;
+
+ NSSize sz;
+ sz.width = 5;
+ sz.height = 5;
+
+ NSImage* dragImage;
+ dragImage = [[NSImage alloc] initWithSize: sz];
+
+ NSRect bounds;
+ bounds.origin = NSMakePoint(0,0);
+ bounds.size = sz;
+
+ [dragImage lockFocus];
+ [[NSColor blackColor] set];
+ [NSBezierPath fillRect: bounds];
+ [dragImage unlockFocus];
+
+ NSPoint pInWnd = [mLastMouseEventBeforeStartDrag locationInWindow];
+ NSPoint p;
+ p = [mView convertPoint: pInWnd fromView: nil];
+ p.x = p.x - sz.width/2;
+ p.y = p.y - sz.height/2;
+
+ // reset drop success flags
+ g_DropSuccessSet = false;
+ g_DropSuccess = false;
+
+ [mView dragImage: dragImage
+ at: p
+ offset: NSMakeSize(0,0)
+ event: mLastMouseEventBeforeStartDrag
+ pasteboard: clipb->getPasteboard()
+ source: mDragSourceHelper
+ slideBack: 1];
+
+ [dragImage release];
+
+ g_XTransferable = uno::Reference<XTransferable>();
+ g_DragSourceView = nil;
+
+ // reset drop success flags
+ g_DropSuccessSet = false;
+ g_DropSuccess = false;
+}
+
+
+// In order to initiate a D&D operation we need to
+// provide the triggering mouse event which we get
+// from the SalFrameView that is associated with
+// this DragSource
+void DragSource::saveMouseEvent(NSEvent* theEvent)
+{
+ if (mLastMouseEventBeforeStartDrag != nil)
+ {
+ [mLastMouseEventBeforeStartDrag release];
+ }
+
+ mLastMouseEventBeforeStartDrag = theEvent;
+}
+
+
+/* isLocal indicates whether or not the DnD operation is OOo
+ internal.
+ */
+unsigned int DragSource::getSupportedDragOperations(bool isLocal) const
+{
+ unsigned int srcActions = OfficeToSystemDragActions(mDragSourceActions);
+
+ if (isLocal)
+ {
+ // Support NSDragOperation generic which means we can
+ // decide which D&D operation to choose. We map
+ // NSDragOperationGenric to DNDConstants::ACTION_DEFAULT
+ // in SystemToOfficeDragActions to signal this and
+ // use it in DropTarget::determineDropAction
+ srcActions |= NSDragOperationGeneric;
+ }
+ else
+ {
+ // Mask out link and move operations on external DnD
+ srcActions &= ~(NSDragOperationMove | NSDragOperationLink);
+ }
+
+ return srcActions;
+}
+
+
+//################################
+// XServiceInfo
+//################################
+
+OUString SAL_CALL DragSource::getImplementationName( ) throw (RuntimeException)
+{
+ return dragSource_getImplementationName();
+}
+
+
+sal_Bool SAL_CALL DragSource::supportsService( const OUString& ServiceName ) throw (RuntimeException)
+{
+ return ServiceName == "com.sun.star.datatransfer.dnd.OleDragSource";
+}
+
+
+Sequence< OUString > SAL_CALL DragSource::getSupportedServiceNames() throw (RuntimeException)
+{
+ return dragSource_getSupportedServiceNames();
+}
+
+
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/DragSource.hxx b/vcl/osx/DragSource.hxx
new file mode 100644
index 000000000000..625388e87a69
--- /dev/null
+++ b/vcl/osx/DragSource.hxx
@@ -0,0 +1,135 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_DRAGSOURCE_HXX
+#define INCLUDED_VCL_OSX_DRAGSOURCE_HXX
+
+#include <com/sun/star/datatransfer/dnd/XDragSource.hpp>
+#include <com/sun/star/datatransfer/dnd/XDragSourceContext.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <cppuhelper/compbase3.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <cppuhelper/basemutex.hxx>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <osl/thread.h>
+#include <com/sun/star/awt/MouseEvent.hpp>
+
+#include <boost/utility.hpp>
+
+#include <premac.h>
+#import <Cocoa/Cocoa.h>
+#include <postmac.h>
+
+
+class DragSource;
+class AquaSalFrame;
+
+/* The functions declared in this protocol are actually
+ declared in vcl/inc/osx/salframe.h. Because we want
+ to avoid importing VCL headers in UNO services and
+ on the other hand want to avoid warnings caused by
+ gcc complaining about unknowness of these functions
+ we declare them in a protocol here and cast at the
+ appropriate places.
+*/
+@protocol MouseEventListener
+-(void)registerMouseEventListener:(id)theHandler;
+-(void)unregisterMouseEventListener:(id)theHandler;
+@end
+
+
+@interface DragSourceHelper : NSObject
+{
+ DragSource* mDragSource;
+}
+
+-(DragSourceHelper*)initWithDragSource: (DragSource*) pds;
+
+-(void)mouseDown: (NSEvent*)theEvent;
+-(void)mouseDragged: (NSEvent*)theEvent;
+
+-(unsigned int)draggingSourceOperationMaskForLocal:(BOOL)isLocal;
+-(void)draggedImage:(NSImage*)anImage beganAt:(NSPoint)aPoint;
+-(void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation;
+-(void)draggedImage:(NSImage *)draggedImage movedTo:(NSPoint)screenPoint;
+
+@end
+
+
+class DragSource : public ::cppu::BaseMutex,
+ public ::cppu::WeakComponentImplHelper3< com::sun::star::datatransfer::dnd::XDragSource,
+ com::sun::star::lang::XInitialization,
+ com::sun::star::lang::XServiceInfo >,
+ private ::boost::noncopyable
+{
+public:
+ DragSource();
+ virtual ~DragSource();
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const com::sun::star::uno::Sequence< com::sun::star::uno::Any >& aArguments )
+ throw(com::sun::star::uno::Exception/*, com::sun::star::uno::RuntimeException*/);
+
+ // XDragSource
+ virtual sal_Bool SAL_CALL isDragImageSupported( ) throw(com::sun::star::uno::RuntimeException);
+
+ virtual sal_Int32 SAL_CALL getDefaultCursor(sal_Int8 dragAction)
+ throw(com::sun::star::lang::IllegalArgumentException, com::sun::star::uno::RuntimeException);
+
+ virtual void SAL_CALL startDrag( const com::sun::star::datatransfer::dnd::DragGestureEvent& trigger,
+ sal_Int8 sourceActions,
+ sal_Int32 cursor,
+ sal_Int32 image,
+ const com::sun::star::uno::Reference< com::sun::star::datatransfer::XTransferable >& transferable,
+ const com::sun::star::uno::Reference< com::sun::star::datatransfer::dnd::XDragSourceListener >& listener )
+ throw(com::sun::star::uno::RuntimeException);
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() throw (com::sun::star::uno::RuntimeException);
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) throw (com::sun::star::uno::RuntimeException);
+ virtual com::sun::star::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() throw (com::sun::star::uno::RuntimeException);
+
+ virtual void saveMouseEvent(NSEvent* theEvent);
+ virtual unsigned int getSupportedDragOperations(bool isLocal) const;
+
+public:
+ // The context notifies the XDragSourceListeners
+ com::sun::star::uno::Reference< com::sun::star::datatransfer::dnd::XDragSourceContext > mXCurrentContext;
+
+ id mView;
+ AquaSalFrame* mpFrame;
+ NSEvent* mLastMouseEventBeforeStartDrag;
+ DragSourceHelper* mDragSourceHelper;
+ com::sun::star::awt::MouseEvent mMouseEvent;
+ com::sun::star::uno::Reference< com::sun::star::datatransfer::XTransferable > mXTransferable;
+ com::sun::star::uno::Reference< com::sun::star::datatransfer::dnd::XDragSourceListener > mXDragSrcListener;
+ // The mouse button that set off the drag and drop operation
+ short m_MouseButton;
+ sal_Int8 mDragSourceActions;
+
+ static com::sun::star::uno::Reference< com::sun::star::datatransfer::XTransferable > g_XTransferable;
+ static NSView* g_DragSourceView;
+ static bool g_DropSuccessSet;
+ static bool g_DropSuccess;
+
+};
+
+#endif // INCLUDED_VCL_OSX_DRAGSOURCE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/DragSourceContext.cxx b/vcl/osx/DragSourceContext.cxx
new file mode 100644
index 000000000000..1eb4fe3ac78e
--- /dev/null
+++ b/vcl/osx/DragSourceContext.cxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
+
+#include "DragSourceContext.hxx"
+
+
+using namespace com::sun::star::datatransfer::dnd;
+using namespace com::sun::star::datatransfer::dnd::DNDConstants;
+using namespace com::sun::star::uno;
+using namespace cppu;
+
+DragSourceContext::DragSourceContext() :
+ WeakComponentImplHelper1<XDragSourceContext>(m_aMutex)
+{
+}
+
+DragSourceContext::~DragSourceContext()
+{
+}
+
+sal_Int32 SAL_CALL DragSourceContext::getCurrentCursor( )
+ throw( RuntimeException)
+{
+ return 0;
+}
+
+void SAL_CALL DragSourceContext::setCursor( sal_Int32 /*cursorId*/ )
+ throw( RuntimeException)
+{
+}
+
+void SAL_CALL DragSourceContext::setImage( sal_Int32 /*imageId*/ )
+ throw( RuntimeException)
+{
+}
+
+void SAL_CALL DragSourceContext::transferablesFlavorsChanged( )
+ throw( RuntimeException)
+{
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/DragSourceContext.hxx b/vcl/osx/DragSourceContext.hxx
new file mode 100644
index 000000000000..f27518ee2756
--- /dev/null
+++ b/vcl/osx/DragSourceContext.hxx
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_DRAGSOURCECONTEXT_HXX
+#define INCLUDED_VCL_OSX_DRAGSOURCECONTEXT_HXX
+
+#include <cppuhelper/implbase1.hxx>
+#include <com/sun/star/datatransfer/dnd/XDragSourceContext.hpp>
+#include <cppuhelper/compbase1.hxx>
+#include <cppuhelper/basemutex.hxx>
+
+#include <boost/utility.hpp>
+
+// This class fires events to XDragSourceListener implementations.
+// Of that interface only dragDropEnd and dropActionChanged are called.
+// The functions dragEnter, dragExit and dragOver are not supported
+// currently.
+// An instance of SourceContext only lives as long as the drag and drop
+// operation lasts.
+class DragSourceContext: public cppu::BaseMutex,
+ public cppu::WeakComponentImplHelper1<com::sun::star::datatransfer::dnd::XDragSourceContext>,
+ private ::boost::noncopyable
+{
+public:
+ DragSourceContext();
+ ~DragSourceContext();
+
+ virtual sal_Int32 SAL_CALL getCurrentCursor( )
+ throw( com::sun::star::uno::RuntimeException);
+
+ virtual void SAL_CALL setCursor( sal_Int32 cursorId )
+ throw( com::sun::star::uno::RuntimeException);
+
+ virtual void SAL_CALL setImage( sal_Int32 imageId )
+ throw( com::sun::star::uno::RuntimeException);
+
+ virtual void SAL_CALL transferablesFlavorsChanged( )
+ throw( com::sun::star::uno::RuntimeException);
+};
+
+#endif // INCLUDED_VCL_OSX_DRAGSOURCECONTEXT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/DropTarget.cxx b/vcl/osx/DropTarget.cxx
new file mode 100644
index 000000000000..e4d3a6bc72a4
--- /dev/null
+++ b/vcl/osx/DropTarget.cxx
@@ -0,0 +1,596 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
+#include <com/sun/star/datatransfer/XTransferable.hpp>
+#include <com/sun/star/datatransfer/dnd/DropTargetDragEnterEvent.hpp>
+
+#include "comphelper/makesequence.hxx"
+#include <cppuhelper/interfacecontainer.hxx>
+
+#include "clipboard.hxx"
+#include "DropTarget.hxx"
+#include "DragActionConversion.hxx"
+
+#include "DragSource.hxx"
+
+#include <rtl/ustring.h>
+#include <stdio.h>
+
+#include <premac.h>
+#include <Carbon/Carbon.h>
+#include <postmac.h>
+
+#include <osx/salframe.h>
+#include <osx/salframeview.h>
+
+using namespace cppu;
+using namespace osl;
+using namespace com::sun::star::datatransfer;
+using namespace com::sun::star::datatransfer::dnd;
+using namespace com::sun::star::datatransfer::dnd::DNDConstants;
+using namespace com::sun::star::datatransfer::clipboard;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::uno;
+using namespace com::sun::star;
+using namespace comphelper;
+
+
+OUString dropTarget_getImplementationName()
+{
+ return OUString("com.sun.star.comp.datatransfer.dnd.OleDropTarget_V1");
+}
+
+
+Sequence<OUString> dropTarget_getSupportedServiceNames()
+{
+ return makeSequence(OUString("com.sun.star.datatransfer.dnd.OleDropTarget"));
+}
+
+
+namespace /* private */
+{
+ // Cocoa's coordinate system has its origin lower-left, VCL's
+ // coordinate system upper-left hence we need to transform
+ // coordinates
+
+ inline void CocoaToVCL(NSPoint& rPoint, const NSRect& bounds)
+ {
+ rPoint.y = bounds.size.height - rPoint.y;
+ }
+}
+
+
+@implementation DropTargetHelper
+
+
+-(DropTargetHelper*)initWithDropTarget:(DropTarget*)pdt
+{
+ self = [super init];
+
+ if (self)
+ {
+ mDropTarget = pdt;
+ }
+
+ return self;
+}
+
+
+-(NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
+{
+ return mDropTarget->draggingEntered(sender);
+}
+
+
+-(NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
+{
+ return mDropTarget->draggingUpdated(sender);
+}
+
+
+-(void)draggingExited:(id <NSDraggingInfo>)sender
+{
+ mDropTarget->draggingExited(sender);
+}
+
+
+-(BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
+{
+ return mDropTarget->prepareForDragOperation(sender);
+}
+
+
+-(BOOL)performDragOperation:(id <NSDraggingInfo>)sender
+{
+ return mDropTarget->performDragOperation(sender);
+}
+
+
+-(void)concludeDragOperation:(id <NSDraggingInfo>)sender
+{
+ mDropTarget->concludeDragOperation(sender);
+}
+
+
+@end
+
+
+DropTarget::DropTarget() :
+ WeakComponentImplHelper5<XInitialization, XDropTarget, XDropTargetDragContext, XDropTargetDropContext, XServiceInfo>(m_aMutex),
+ mView(nil),
+ mpFrame(NULL),
+ mDropTargetHelper(nil),
+ mbActive(false),
+ mDragSourceSupportedActions(DNDConstants::ACTION_NONE),
+ mSelectedDropAction(DNDConstants::ACTION_NONE),
+ mDefaultActions(DNDConstants::ACTION_COPY_OR_MOVE | DNDConstants::ACTION_LINK | DNDConstants::ACTION_DEFAULT)
+{
+ mDataFlavorMapper = DataFlavorMapperPtr_t(new DataFlavorMapper());
+}
+
+
+DropTarget::~DropTarget()
+{
+ if( AquaSalFrame::isAlive( mpFrame ) )
+ [(id <DraggingDestinationHandler>)mView unregisterDraggingDestinationHandler:mDropTargetHelper];
+ [mDropTargetHelper release];
+}
+
+
+sal_Int8 DropTarget::determineDropAction(sal_Int8 dropActions, id sender) const
+{
+ sal_Int8 dropAct = dropActions;
+ bool srcAndDestEqual = false;
+
+ if ([sender draggingSource] != nil)
+ {
+ // Internal DnD
+ NSView* destView = [[sender draggingDestinationWindow] contentView];
+ srcAndDestEqual = (DragSource::g_DragSourceView == destView);
+ }
+
+ // If ACTION_DEFAULT is set this means NSDragOperationGeneric
+ // has been set and we map this to ACTION_MOVE or ACTION_COPY
+ // depending on whether or not source and dest are equal,
+ // this hopefully satisfies all parties
+ if( (dropActions == DNDConstants::ACTION_DEFAULT)
+ || ((dropActions == mDragSourceSupportedActions)
+ && !(~mDragSourceSupportedActions & DNDConstants::ACTION_COPY_OR_MOVE ) ) )
+ {
+ dropAct = srcAndDestEqual ? DNDConstants::ACTION_MOVE :
+ DNDConstants::ACTION_COPY;
+ }
+ // if more than one drop actions have been specified
+ // set ACTION_DEFAULT in order to let the drop target
+ // decide which one to use
+ else if (dropActions != DNDConstants::ACTION_NONE &&
+ dropActions != DNDConstants::ACTION_MOVE &&
+ dropActions != DNDConstants::ACTION_COPY &&
+ dropActions != DNDConstants::ACTION_LINK)
+ {
+ if (srcAndDestEqual)
+ {
+ dropAct = dropActions;
+ }
+ else // source and destination are different
+ {
+ if (dropActions & DNDConstants::ACTION_COPY)
+ dropAct = DNDConstants::ACTION_COPY;
+ else if (dropActions & DNDConstants::ACTION_MOVE)
+ dropAct = DNDConstants::ACTION_MOVE;
+ else if (dropActions & DNDConstants::ACTION_LINK)
+ dropAct = DNDConstants::ACTION_LINK;
+ }
+
+ dropAct |= DNDConstants::ACTION_DEFAULT;
+ }
+
+ return dropAct;
+}
+
+
+NSDragOperation DropTarget::draggingEntered(id sender)
+{
+ // Initially when DnD will be started no modifier key can be pressed yet
+ // thus we are getting all actions that the drag source supports, we save
+ // this value because later the system masks the drag source actions if
+ // a modifier key will be pressed
+ mDragSourceSupportedActions = SystemToOfficeDragActions([sender draggingSourceOperationMask]);
+
+ // Only if the drop target is really interessted in the drag actions
+ // supported by the source
+ if (mDragSourceSupportedActions & mDefaultActions)
+ {
+ sal_Int8 currentAction = determineDropAction(mDragSourceSupportedActions, sender);
+
+ NSRect bounds = [mView bounds];
+ NSPoint dragLocation = [sender draggedImageLocation];
+
+ CocoaToVCL(dragLocation, bounds);
+
+ sal_Int32 posX = static_cast<sal_Int32>(dragLocation.x);
+ sal_Int32 posY = static_cast<sal_Int32>(dragLocation.y);
+
+ NSPasteboard* dragPboard = [sender draggingPasteboard];
+ mXCurrentDragClipboard = new AquaClipboard(dragPboard, false);
+
+ uno::Reference<XTransferable> xTransferable = DragSource::g_XTransferable.is() ?
+ DragSource::g_XTransferable : mXCurrentDragClipboard->getContents();
+
+ DropTargetDragEnterEvent dtdee(static_cast<OWeakObject*>(this),
+ 0,
+ this,
+ currentAction,
+ posX,
+ posY,
+ mDragSourceSupportedActions,
+ xTransferable->getTransferDataFlavors());
+
+ fire_dragEnter(dtdee);
+ }
+
+ return OfficeToSystemDragActions(mSelectedDropAction);
+}
+
+
+NSDragOperation DropTarget::draggingUpdated(id sender)
+{
+ sal_Int8 currentDragSourceActions =
+ SystemToOfficeDragActions([sender draggingSourceOperationMask]);
+ NSDragOperation dragOp = NSDragOperationNone;
+
+ if (currentDragSourceActions & mDefaultActions)
+ {
+ sal_Int8 currentAction = determineDropAction(currentDragSourceActions, sender);
+ NSRect bounds = [mView bounds];
+ NSPoint dragLocation = [sender draggedImageLocation];
+
+ CocoaToVCL(dragLocation, bounds);
+
+ sal_Int32 posX = static_cast<sal_Int32>(dragLocation.x);
+ sal_Int32 posY = static_cast<sal_Int32>(dragLocation.y);
+
+ DropTargetDragEvent dtde(static_cast<OWeakObject*>(this),
+ 0,
+ this,
+ currentAction,
+ posX,
+ posY,
+ mDragSourceSupportedActions);
+
+ fire_dragOver(dtde);
+
+ // drag over callbacks likely have rendered something
+ [mView setNeedsDisplay: TRUE];
+
+ dragOp = OfficeToSystemDragActions(mSelectedDropAction);
+
+ //NSLog(@"Drag update: Source actions: %x proposed action %x selected action %x", mDragSourceSupportedActions, currentAction, mSelectedDropAction);
+ }
+
+#ifndef __LP64__
+ // Weird but it appears as if there is no method in Cocoa
+ // to create a kThemeCopyArrowCursor hence we have to use
+ // Carbon to do it
+ if (dragOp == NSDragOperationNone)
+ SetThemeCursor(kThemeNotAllowedCursor);
+ else if (dragOp == NSDragOperationCopy)
+ SetThemeCursor(kThemeCopyArrowCursor);
+ else
+ SetThemeCursor(kThemeArrowCursor);
+#else
+ // FIXME: SetThemeCursor replacement?
+#endif
+ return dragOp;
+}
+
+
+void DropTarget::draggingExited(id /*sender*/)
+{
+ DropTargetEvent dte(static_cast<OWeakObject*>(this), 0);
+ fire_dragExit(dte);
+ mDragSourceSupportedActions = DNDConstants::ACTION_NONE;
+ mSelectedDropAction = DNDConstants::ACTION_NONE;
+#ifndef __LP64__
+ SetThemeCursor(kThemeArrowCursor);
+#endif
+}
+
+
+BOOL DropTarget::prepareForDragOperation(id /*sender*/)
+{
+ return 1;
+}
+
+
+BOOL DropTarget::performDragOperation(id sender)
+{
+ bool bSuccess = false;
+
+ if (mSelectedDropAction != DNDConstants::ACTION_NONE)
+ {
+ uno::Reference<XTransferable> xTransferable = DragSource::g_XTransferable;
+
+ if (!DragSource::g_XTransferable.is())
+ {
+ xTransferable = mXCurrentDragClipboard->getContents();
+ }
+
+ NSRect bounds = [mView bounds];
+ NSPoint dragLocation = [sender draggedImageLocation];
+
+ CocoaToVCL(dragLocation, bounds);
+
+ sal_Int32 posX = static_cast<sal_Int32>(dragLocation.x);
+ sal_Int32 posY = static_cast<sal_Int32>(dragLocation.y);
+
+ DropTargetDropEvent dtde(static_cast<OWeakObject*>(this),
+ 0,
+ this,
+ mSelectedDropAction,
+ posX,
+ posY,
+ mDragSourceSupportedActions,
+ xTransferable);
+
+ fire_drop(dtde);
+
+ bSuccess = true;
+ }
+
+ return bSuccess;
+}
+
+
+void DropTarget::concludeDragOperation(id /*sender*/)
+{
+ mDragSourceSupportedActions = DNDConstants::ACTION_NONE;
+ mSelectedDropAction = DNDConstants::ACTION_NONE;
+ mXCurrentDragClipboard = uno::Reference<XClipboard>();
+#ifndef __LP64__
+ SetThemeCursor(kThemeArrowCursor);
+#endif
+}
+
+
+ // called from WeakComponentImplHelperX::dispose
+ // WeakComponentImplHelper calls disposing before it destroys
+ // itself.
+ void SAL_CALL DropTarget::disposing()
+ {
+ }
+
+
+ void SAL_CALL DropTarget::initialize(const Sequence< Any >& aArguments)
+ throw(Exception)
+ {
+ if (aArguments.getLength() < 2)
+ {
+ throw RuntimeException("DropTarget::initialize: Cannot install window event handler",
+ static_cast<OWeakObject*>(this));
+ }
+
+ Any pNSView = aArguments[0];
+ sal_uInt64 tmp = 0;
+ pNSView >>= tmp;
+ mView = (id)tmp;
+ mpFrame = [(SalFrameView*)mView getSalFrame];
+
+ mDropTargetHelper = [[DropTargetHelper alloc] initWithDropTarget: this];
+
+ [(id <DraggingDestinationHandler>)mView registerDraggingDestinationHandler:mDropTargetHelper];
+ [mView registerForDraggedTypes: mDataFlavorMapper->getAllSupportedPboardTypes()];
+
+ id wnd = [mView window];
+ NSWindow* parentWnd = [wnd parentWindow];
+ unsigned int topWndStyle = (NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask);
+ unsigned int wndStyles = [wnd styleMask] & topWndStyle;
+
+ if (parentWnd == nil && (wndStyles == topWndStyle))
+ {
+ [wnd registerDraggingDestinationHandler:mDropTargetHelper];
+ [wnd registerForDraggedTypes: [NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
+ }
+ }
+
+
+ void SAL_CALL DropTarget::addDropTargetListener(const uno::Reference<XDropTargetListener>& dtl)
+ throw(RuntimeException)
+ {
+ rBHelper.addListener(::getCppuType(&dtl), dtl);
+ }
+
+
+ void SAL_CALL DropTarget::removeDropTargetListener(const uno::Reference<XDropTargetListener>& dtl)
+ throw(RuntimeException)
+ {
+ rBHelper.removeListener(::getCppuType(&dtl), dtl);
+ }
+
+
+ sal_Bool SAL_CALL DropTarget::isActive( ) throw(RuntimeException)
+ {
+ return mbActive;
+ }
+
+
+ void SAL_CALL DropTarget::setActive(sal_Bool active) throw(RuntimeException)
+ {
+ mbActive = active;
+ }
+
+
+ sal_Int8 SAL_CALL DropTarget::getDefaultActions() throw(RuntimeException)
+ {
+ return mDefaultActions;
+ }
+
+
+ void SAL_CALL DropTarget::setDefaultActions(sal_Int8 actions) throw(RuntimeException)
+ {
+ OSL_ENSURE( actions < 8, "No valid default actions");
+ mDefaultActions= actions;
+ }
+
+
+ // XDropTargetDragContext
+
+ void SAL_CALL DropTarget::acceptDrag(sal_Int8 dragOperation) throw (RuntimeException)
+ {
+ mSelectedDropAction = dragOperation;
+ }
+
+
+ void SAL_CALL DropTarget::rejectDrag() throw (RuntimeException)
+ {
+ mSelectedDropAction = DNDConstants::ACTION_NONE;
+ }
+
+
+ //XDropTargetDropContext
+
+ void SAL_CALL DropTarget::acceptDrop(sal_Int8 dropOperation) throw( RuntimeException)
+ {
+ mSelectedDropAction = dropOperation;
+ }
+
+
+ void SAL_CALL DropTarget::rejectDrop() throw (RuntimeException)
+ {
+ mSelectedDropAction = DNDConstants::ACTION_NONE;
+ }
+
+
+ void SAL_CALL DropTarget::dropComplete(sal_Bool success) throw (RuntimeException)
+ {
+ // Reset the internal transferable used as shortcut in case this is
+ // an internal D&D operation
+ DragSource::g_XTransferable = uno::Reference<XTransferable>();
+ DragSource::g_DropSuccessSet = true;
+ DragSource::g_DropSuccess = success;
+ }
+
+
+ void DropTarget::fire_drop( const DropTargetDropEvent& dte)
+ {
+ OInterfaceContainerHelper* pContainer= rBHelper.getContainer( getCppuType( (uno::Reference<XDropTargetListener>* )0 ) );
+ if( pContainer)
+ {
+ OInterfaceIteratorHelper iter( *pContainer);
+ while( iter.hasMoreElements())
+ {
+ uno::Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
+
+ try { listener->drop( dte); }
+ catch(RuntimeException&) {}
+ }
+ }
+ }
+
+
+ void DropTarget::fire_dragEnter(const DropTargetDragEnterEvent& e)
+ {
+ OInterfaceContainerHelper* pContainer= rBHelper.getContainer( getCppuType( (uno::Reference<XDropTargetListener>* )0 ) );
+ if( pContainer)
+ {
+ OInterfaceIteratorHelper iter( *pContainer);
+ while( iter.hasMoreElements())
+ {
+ uno::Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
+
+ try { listener->dragEnter( e); }
+ catch (RuntimeException&) {}
+ }
+ }
+ }
+
+
+ void DropTarget::fire_dragExit(const DropTargetEvent& dte)
+ {
+ OInterfaceContainerHelper* pContainer= rBHelper.getContainer( getCppuType( (uno::Reference<XDropTargetListener>* )0 ) );
+
+ if( pContainer)
+ {
+ OInterfaceIteratorHelper iter( *pContainer);
+ while( iter.hasMoreElements())
+ {
+ uno::Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
+
+ try { listener->dragExit( dte); }
+ catch (RuntimeException&) {}
+ }
+ }
+ }
+
+
+ void DropTarget::fire_dragOver(const DropTargetDragEvent& dtde)
+ {
+ OInterfaceContainerHelper* pContainer= rBHelper.getContainer( getCppuType( (uno::Reference<XDropTargetListener>* )0 ) );
+ if( pContainer)
+ {
+ OInterfaceIteratorHelper iter( *pContainer );
+ while( iter.hasMoreElements())
+ {
+ uno::Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
+
+ try { listener->dragOver( dtde); }
+ catch (RuntimeException&) {}
+ }
+ }
+ }
+
+
+ void DropTarget::fire_dropActionChanged(const DropTargetDragEvent& dtde)
+ {
+ OInterfaceContainerHelper* pContainer= rBHelper.getContainer( getCppuType( (uno::Reference<XDropTargetListener>* )0 ) );
+ if( pContainer)
+ {
+ OInterfaceIteratorHelper iter( *pContainer);
+ while( iter.hasMoreElements())
+ {
+ uno::Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
+
+ try { listener->dropActionChanged( dtde); }
+ catch (RuntimeException&) {}
+ }
+ }
+ }
+
+
+ // XServiceInfo
+
+ OUString SAL_CALL DropTarget::getImplementationName() throw (RuntimeException)
+ {
+ return dropTarget_getImplementationName();
+ }
+
+
+ sal_Bool SAL_CALL DropTarget::supportsService( const OUString& ServiceName ) throw (RuntimeException)
+ {
+ return ServiceName == "com.sun.star.datatransfer.dnd.OleDropTarget";
+ }
+
+
+ Sequence< OUString > SAL_CALL DropTarget::getSupportedServiceNames( ) throw (RuntimeException)
+ {
+ return dropTarget_getSupportedServiceNames();
+ }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/DropTarget.hxx b/vcl/osx/DropTarget.hxx
new file mode 100644
index 000000000000..0eea030db18b
--- /dev/null
+++ b/vcl/osx/DropTarget.hxx
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_DROPTARGET_HXX
+#define INCLUDED_VCL_OSX_DROPTARGET_HXX
+
+#include "DataFlavorMapping.hxx"
+#include <cppuhelper/compbase5.hxx>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/datatransfer/dnd/XDropTarget.hpp>
+
+#include <com/sun/star/datatransfer/dnd/XDropTargetListener.hpp>
+#include <com/sun/star/datatransfer/dnd/DropTargetDragEnterEvent.hpp>
+#include <com/sun/star/datatransfer/dnd/XDropTargetDragContext.hpp>
+#include <com/sun/star/datatransfer/dnd/XDropTargetDropContext.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <cppuhelper/basemutex.hxx>
+#include <com/sun/star/lang/XMultiComponentFactory.hpp>
+
+#include <boost/utility.hpp>
+
+#include <premac.h>
+#import <Cocoa/Cocoa.h>
+#include <postmac.h>
+
+class DropTarget;
+class AquaSalFrame;
+
+/* The functions declared in this protocol are actually
+ declared in vcl/inc/osx/salframe.h. Because we want
+ to avoid importing VCL headers in UNO services and
+ on the other hand want to avoid warnings caused by
+ gcc complaining about unknowness of these functions
+ we declare them in a protocol here and cast at the
+ appropriate places.
+*/
+@protocol DraggingDestinationHandler
+-(void)registerDraggingDestinationHandler:(id)theHandler;
+-(void)unregisterDraggingDestinationHandler:(id)theHandler;
+@end
+
+
+@interface DropTargetHelper : NSObject
+{
+ DropTarget* mDropTarget;
+}
+
+-(DropTargetHelper*)initWithDropTarget:(DropTarget*)pdt;
+
+-(NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender;
+-(NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender;
+-(void)draggingExited:(id <NSDraggingInfo>)sender;
+-(BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender;
+-(BOOL)performDragOperation:(id <NSDraggingInfo>)sender;
+-(void)concludeDragOperation:(id <NSDraggingInfo>)sender;
+
+@end
+
+
+class DropTarget: public cppu::BaseMutex,
+ public cppu::WeakComponentImplHelper5< com::sun::star::lang::XInitialization,
+ com::sun::star::datatransfer::dnd::XDropTarget,
+ com::sun::star::datatransfer::dnd::XDropTargetDragContext,
+ com::sun::star::datatransfer::dnd::XDropTargetDropContext,
+ com::sun::star::lang::XServiceInfo >,
+ private boost::noncopyable
+{
+public:
+ DropTarget();
+ virtual ~DropTarget();
+
+ // Overrides WeakComponentImplHelper::disposing which is called by
+ // WeakComponentImplHelper::dispose
+ // Must be called.
+ virtual void SAL_CALL disposing();
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const com::sun::star::uno::Sequence< com::sun::star::uno::Any >& aArguments )
+ throw(com::sun::star::uno::Exception);
+
+ // XDropTarget
+ virtual void SAL_CALL addDropTargetListener( const com::sun::star::uno::Reference< com::sun::star::datatransfer::dnd::XDropTargetListener >& dtl )
+ throw(com::sun::star::uno::RuntimeException);
+
+ virtual void SAL_CALL removeDropTargetListener( const com::sun::star::uno::Reference< com::sun::star::datatransfer::dnd::XDropTargetListener >& dtl )
+ throw(com::sun::star::uno::RuntimeException);
+
+ // Default is not active
+ virtual sal_Bool SAL_CALL isActive() throw(com::sun::star::uno::RuntimeException);
+ virtual void SAL_CALL setActive(sal_Bool isActive) throw(com::sun::star::uno::RuntimeException);
+ virtual sal_Int8 SAL_CALL getDefaultActions() throw(com::sun::star::uno::RuntimeException);
+ virtual void SAL_CALL setDefaultActions(sal_Int8 actions) throw(com::sun::star::uno::RuntimeException);
+
+ // XDropTargetDragContext
+ virtual void SAL_CALL acceptDrag(sal_Int8 dragOperation) throw(com::sun::star::uno::RuntimeException);
+ virtual void SAL_CALL rejectDrag() throw(com::sun::star::uno::RuntimeException);
+
+ // XDropTargetDragContext
+ virtual void SAL_CALL acceptDrop(sal_Int8 dropOperation) throw (com::sun::star::uno::RuntimeException);
+ virtual void SAL_CALL rejectDrop() throw (com::sun::star::uno::RuntimeException);
+ virtual void SAL_CALL dropComplete(sal_Bool success) throw (com::sun::star::uno::RuntimeException);
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() throw (com::sun::star::uno::RuntimeException);
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) throw (com::sun::star::uno::RuntimeException);
+ virtual com::sun::star::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() throw (com::sun::star::uno::RuntimeException);
+
+ // NSDraggingDestination protocol functions
+ virtual NSDragOperation draggingEntered(id sender);
+ virtual NSDragOperation draggingUpdated(id sender);
+ virtual void draggingExited(id sender);
+ virtual BOOL prepareForDragOperation(id sender);
+ virtual BOOL performDragOperation(id sender);
+ virtual void concludeDragOperation(id sender);
+
+ /* If multiple actions are supported by the drag source and
+ the user did not choose a specific action by pressing a
+ modifier key choose a default action to be proposed to
+ the application.
+ */
+ sal_Int8 determineDropAction(sal_Int8 dropActions, id sender) const;
+
+private:
+ void fire_drop(const com::sun::star::datatransfer::dnd::DropTargetDropEvent& dte);
+ void fire_dragEnter(const com::sun::star::datatransfer::dnd::DropTargetDragEnterEvent& dtdee);
+ void fire_dragExit(const com::sun::star::datatransfer::dnd::DropTargetEvent& dte);
+ void fire_dragOver(const com::sun::star::datatransfer::dnd::DropTargetDragEvent& dtde);
+ void fire_dropActionChanged(const com::sun::star::datatransfer::dnd::DropTargetDragEvent& dtde);
+
+private:
+ com::sun::star::uno::Reference< com::sun::star::datatransfer::dnd::XDropTargetDragContext > mXCurrentDragContext;
+ com::sun::star::uno::Reference< com::sun::star::datatransfer::dnd::XDropTargetDropContext > mXCurrentDropContext;
+ com::sun::star::uno::Reference< com::sun::star::datatransfer::clipboard::XClipboard > mXCurrentDragClipboard;
+ DataFlavorMapperPtr_t mDataFlavorMapper;
+ id mView;
+ AquaSalFrame* mpFrame;
+ DropTargetHelper* mDropTargetHelper;
+ bool mbActive;
+ sal_Int8 mDragSourceSupportedActions;
+ sal_Int8 mSelectedDropAction;
+ sal_Int8 mDefaultActions;
+};
+
+#endif // INCLUDED_VCL_OSX_DROPTARGET_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/HtmlFmtFlt.cxx b/vcl/osx/HtmlFmtFlt.cxx
new file mode 100644
index 000000000000..e629f11e982e
--- /dev/null
+++ b/vcl/osx/HtmlFmtFlt.cxx
@@ -0,0 +1,168 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "HtmlFmtFlt.hxx"
+
+#include <rtl/string.h>
+
+#include <string>
+#include <sstream>
+#include <vector>
+#include <iomanip>
+
+#include <boost/assert.hpp>
+
+using namespace com::sun::star::uno;
+
+//------------------------------------------------------------------------------
+// converts the openoffice text/html clipboard format to the HTML Format
+// well known under MS Windows
+// the MS HTML Format has a header before the real html data
+//
+// Version:1.0 Version number of the clipboard. Staring is 0.9
+// StartHTML: Byte count from the beginning of the clipboard to the start
+// of the context, or -1 if no context
+// EndHTML: Byte count from the beginning of the clipboard to the end
+// of the context, or -1 if no context
+// StartFragment: Byte count from the beginning of the clipboard to the
+// start of the fragment
+// EndFragment: Byte count from the beginning of the clipboard to the
+// end of the fragment
+// StartSelection: Byte count from the beginning of the clipboard to the
+// start of the selection
+// EndSelection: Byte count from the beginning of the clipboard to the
+// end of the selection
+//
+// StartSelection and EndSelection are optional
+// The fragment should be preceded and followed by the HTML comments
+// <!--StartFragment--> and <!--EndFragment--> (no space between !-- and the
+// text
+//------------------------------------------------------------------------------
+
+namespace // private
+{
+std::string GetHtmlFormatHeader(size_t startHtml, size_t endHtml, size_t startFragment, size_t endFragment)
+{
+ std::ostringstream htmlHeader;
+ htmlHeader << "Version:1.0" << '\r' << '\n';
+ htmlHeader << "StartHTML:" << std::setw(10) << std::setfill('0') << std::dec << startHtml << '\r' << '\n';
+ htmlHeader << "EndHTML:" << std::setw(10) << std::setfill('0') << std::dec << endHtml << '\r' << '\n';
+ htmlHeader << "StartFragment:" << std::setw(10) << std::setfill('0') << std::dec << startFragment << '\r' << '\n';
+ htmlHeader << "EndFragment:" << std::setw(10) << std::setfill('0') << std::dec << endFragment << '\r' << '\n';
+ return htmlHeader.str();
+}
+
+} // namespace private
+
+
+// the office always writes the start and end html tag in upper cases and
+// without spaces both tags don't allow parameters
+const std::string TAG_HTML = std::string("<HTML>");
+const std::string TAG_END_HTML = std::string("</HTML>");
+
+// The body tag may have parameters so we need to search for the
+// closing '>' manually e.g. <BODY param> #92840#
+const std::string TAG_BODY = std::string("<BODY");
+const std::string TAG_END_BODY = std::string("</BODY");
+
+Sequence<sal_Int8> SAL_CALL TextHtmlToHTMLFormat(Sequence<sal_Int8>& aTextHtml)
+{
+ OSL_ASSERT(aTextHtml.getLength() > 0);
+
+ if (!(aTextHtml.getLength() > 0))
+ return Sequence<sal_Int8>();
+
+ // fill the buffer with dummy values to calc the exact length
+ std::string dummyHtmlHeader = GetHtmlFormatHeader(0, 0, 0, 0);
+ size_t lHtmlFormatHeader = dummyHtmlHeader.length();
+
+ std::string textHtml(
+ reinterpret_cast<const sal_Char*>(aTextHtml.getConstArray()),
+ reinterpret_cast<const sal_Char*>(aTextHtml.getConstArray()) + aTextHtml.getLength());
+
+ std::string::size_type nStartHtml = textHtml.find(TAG_HTML) + lHtmlFormatHeader - 1; // we start one before '<HTML>' Word 2000 does also so
+ std::string::size_type nEndHtml = textHtml.find(TAG_END_HTML) + lHtmlFormatHeader + TAG_END_HTML.length() + 1; // our SOffice 5.2 wants 2 behind </HTML>?
+
+ // The body tag may have parameters so we need to search for the
+ // closing '>' manually e.g. <BODY param> #92840#
+ std::string::size_type nStartFragment = textHtml.find(">", textHtml.find(TAG_BODY)) + lHtmlFormatHeader + 1;
+ std::string::size_type nEndFragment = textHtml.find(TAG_END_BODY) + lHtmlFormatHeader;
+
+ std::string htmlFormat = GetHtmlFormatHeader(nStartHtml, nEndHtml, nStartFragment, nEndFragment);
+ htmlFormat += textHtml;
+
+ Sequence<sal_Int8> byteSequence(htmlFormat.length() + 1); // space the trailing '\0'
+ memset(byteSequence.getArray(), 0, byteSequence.getLength());
+
+ memcpy(
+ static_cast<void*>(byteSequence.getArray()),
+ static_cast<const void*>(htmlFormat.c_str()),
+ htmlFormat.length());
+
+ return byteSequence;
+}
+
+const char* HtmlStartTag = "<html";
+
+Sequence<sal_Int8> HTMLFormatToTextHtml(const Sequence<sal_Int8>& aHTMLFormat)
+{
+ BOOST_ASSERT(isHTMLFormat(aHTMLFormat) && "No HTML Format provided");
+
+ Sequence<sal_Int8>& nonconstHTMLFormatRef = const_cast< Sequence<sal_Int8>& >(aHTMLFormat);
+ sal_Char* dataStart = reinterpret_cast<sal_Char*>(nonconstHTMLFormatRef.getArray());
+ sal_Char* dataEnd = dataStart + nonconstHTMLFormatRef.getLength() - 1;
+ const sal_Char* htmlStartTag = strcasestr(dataStart, HtmlStartTag);
+
+ BOOST_ASSERT(htmlStartTag && "Seems to be no HTML at all");
+
+ // It doesn't seem to be HTML? Well then simply return what has been
+ // provided in non-debug builds
+ if (htmlStartTag == NULL)
+ {
+ return aHTMLFormat;
+ }
+
+ sal_Int32 len = dataEnd - htmlStartTag;
+ Sequence<sal_Int8> plainHtmlData(len);
+
+ memcpy(static_cast<void*>(plainHtmlData.getArray()), htmlStartTag, len);
+
+ return plainHtmlData;
+}
+
+/* A simple format detection. We are just comparing the first few bytes
+ of the provided byte sequence to see whether or not it is the MS
+ Office Html format. If it shows that this is not reliable enough we
+ can improve this
+*/
+const char HtmlFormatStart[] = "Version:";
+int HtmlFormatStartLen = (sizeof(HtmlFormatStart) - 1);
+
+bool isHTMLFormat(const Sequence<sal_Int8>& aHtmlSequence)
+{
+ if (aHtmlSequence.getLength() < HtmlFormatStartLen)
+ return false;
+
+ return rtl_str_compareIgnoreAsciiCase_WithLength(HtmlFormatStart,
+ HtmlFormatStartLen,
+ reinterpret_cast<const sal_Char*>(aHtmlSequence.getConstArray()),
+ HtmlFormatStartLen) == 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/HtmlFmtFlt.hxx b/vcl/osx/HtmlFmtFlt.hxx
new file mode 100644
index 000000000000..77a92493bad6
--- /dev/null
+++ b/vcl/osx/HtmlFmtFlt.hxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_HTMLFMTFLT_HXX
+#define INCLUDED_VCL_OSX_HTMLFMTFLT_HXX
+
+#include <com/sun/star/uno/Sequence.hxx>
+
+/* Transform plain HTML into the format expected by MS Office.
+ */
+com::sun::star::uno::Sequence<sal_Int8> TextHtmlToHTMLFormat(com::sun::star::uno::Sequence<sal_Int8>& aTextHtml);
+
+/* Transform the MS Office HTML format into plain HTML.
+ */
+com::sun::star::uno::Sequence<sal_Int8> HTMLFormatToTextHtml(const com::sun::star::uno::Sequence<sal_Int8>& aHTMLFormat);
+
+/* Detects whether the given byte sequence contains the MS Office Html format.
+
+ @returns True if the MS Office Html format will be detected False otherwise.
+ */
+bool isHTMLFormat (const com::sun::star::uno::Sequence<sal_Int8>& aHtmlSequence);
+
+#endif // INCLUDED_VCL_OSX_HTMLFMTFLT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/OSXTransferable.cxx b/vcl/osx/OSXTransferable.cxx
new file mode 100644
index 000000000000..7cfffcd4d7c5
--- /dev/null
+++ b/vcl/osx/OSXTransferable.cxx
@@ -0,0 +1,203 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/types.h>
+
+#include "OSXTransferable.hxx"
+
+#include "DataFlavorMapping.hxx"
+
+using namespace std;
+using namespace osl;
+using namespace cppu;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::datatransfer;
+using namespace com::sun::star::io;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::container;
+
+
+namespace // private
+{
+ bool isValidFlavor( const DataFlavor& aFlavor )
+ {
+ size_t len = aFlavor.MimeType.getLength();
+ Type dtype = aFlavor.DataType;
+ return ((len > 0) && ((dtype == getCppuType((Sequence<sal_Int8>*)0)) || (dtype == getCppuType((OUString*)0))));
+ }
+
+} // namespace private
+
+
+OSXTransferable::OSXTransferable(const Reference<XMimeContentTypeFactory> rXMimeCntFactory,
+ DataFlavorMapperPtr_t pDataFlavorMapper,
+ NSPasteboard* pasteboard) :
+ mrXMimeCntFactory(rXMimeCntFactory),
+ mDataFlavorMapper(pDataFlavorMapper),
+ mPasteboard(pasteboard)
+{
+ [mPasteboard retain];
+
+ initClipboardItemList();
+}
+
+
+OSXTransferable::~OSXTransferable()
+{
+ [mPasteboard release];
+}
+
+
+Any SAL_CALL OSXTransferable::getTransferData( const DataFlavor& aFlavor )
+ throw( UnsupportedFlavorException, IOException, RuntimeException )
+{
+ if (!isValidFlavor(aFlavor) || !isDataFlavorSupported(aFlavor))
+ {
+ throw UnsupportedFlavorException("AquaClipboard: Unsupported data flavor",
+ static_cast<XTransferable*>(this));
+ }
+
+ bool bInternal(false);
+ NSString* sysFormat =
+ (aFlavor.MimeType.startsWith("image/png"))
+ ? mDataFlavorMapper->openOfficeImageToSystemFlavor( mPasteboard )
+ : mDataFlavorMapper->openOfficeToSystemFlavor(aFlavor, bInternal);
+ DataProviderPtr_t dp;
+
+ if ([sysFormat caseInsensitiveCompare: NSFilenamesPboardType] == NSOrderedSame)
+ {
+ NSArray* sysData = [mPasteboard propertyListForType: sysFormat];
+ dp = mDataFlavorMapper->getDataProvider(sysFormat, sysData);
+ }
+ else
+ {
+ NSData* sysData = [mPasteboard dataForType: sysFormat];
+ dp = mDataFlavorMapper->getDataProvider(sysFormat, sysData);
+ }
+
+ if (dp.get() == NULL)
+ {
+ throw UnsupportedFlavorException("AquaClipboard: Unsupported data flavor",
+ static_cast<XTransferable*>(this));
+ }
+
+ return dp->getOOoData();
+}
+
+
+bool OSXTransferable::isUnicodeText(const DataFlavor& flavor)
+{
+ return (flavor.DataType == getCppuType((OUString*)0));
+}
+
+
+Sequence< DataFlavor > SAL_CALL OSXTransferable::getTransferDataFlavors( )
+ throw( RuntimeException )
+{
+ return mFlavorList;
+}
+
+
+sal_Bool SAL_CALL OSXTransferable::isDataFlavorSupported(const DataFlavor& aFlavor)
+ throw( RuntimeException )
+{
+ for (sal_Int32 i = 0; i < mFlavorList.getLength(); i++)
+ if (compareDataFlavors(aFlavor, mFlavorList[i]))
+ return sal_True;
+
+ return sal_False;
+}
+
+
+void OSXTransferable::initClipboardItemList()
+{
+ NSArray* pboardFormats = [mPasteboard types];
+
+ if (pboardFormats == NULL)
+ {
+ throw RuntimeException("AquaClipboard: Cannot get clipboard data",
+ static_cast<XTransferable*>(this));
+ }
+
+ mFlavorList = mDataFlavorMapper->typesArrayToFlavorSequence(pboardFormats);
+}
+
+
+/* Compares two DataFlavors. Returns true if both DataFlavor have the same media type
+ and the number of parameter and all parameter values do match otherwise false
+ is returned.
+ */
+bool OSXTransferable::compareDataFlavors(const DataFlavor& lhs, const DataFlavor& rhs )
+{
+ try
+ {
+ Reference<XMimeContentType> xLhs(mrXMimeCntFactory->createMimeContentType(lhs.MimeType));
+ Reference<XMimeContentType> xRhs(mrXMimeCntFactory->createMimeContentType(rhs.MimeType));
+
+ if (!xLhs->getFullMediaType().equalsIgnoreAsciiCase(xRhs->getFullMediaType()) ||
+ !cmpAllContentTypeParameter(xLhs, xRhs))
+ {
+ return false;
+ }
+ }
+ catch( IllegalArgumentException& )
+ {
+ OSL_FAIL( "Invalid content type detected" );
+ return false;
+ }
+
+ return true;
+}
+
+
+bool OSXTransferable::cmpAllContentTypeParameter(const Reference<XMimeContentType> xLhs,
+ const Reference<XMimeContentType> xRhs) const
+{
+ Sequence<OUString> xLhsFlavors = xLhs->getParameters();
+ Sequence<OUString> xRhsFlavors = xRhs->getParameters();
+
+ // Stop here if the number of parameters is different already
+ if (xLhsFlavors.getLength() != xRhsFlavors.getLength())
+ return false;
+
+ try
+ {
+ OUString pLhs;
+ OUString pRhs;
+
+ for (sal_Int32 i = 0; i < xLhsFlavors.getLength(); i++)
+ {
+ pLhs = xLhs->getParameterValue(xLhsFlavors[i]);
+ pRhs = xRhs->getParameterValue(xLhsFlavors[i]);
+
+ if (!pLhs.equalsIgnoreAsciiCase(pRhs))
+ {
+ return false;
+ }
+ }
+ }
+ catch(IllegalArgumentException&)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/OSXTransferable.hxx b/vcl/osx/OSXTransferable.hxx
new file mode 100644
index 000000000000..27885effd03f
--- /dev/null
+++ b/vcl/osx/OSXTransferable.hxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef _TRANSFERABLE_HXX_
+#define _TRANSFERABLE_HXX_
+
+#include <com/sun/star/datatransfer/XTransferable.hpp>
+#include <cppuhelper/implbase1.hxx>
+#include <com/sun/star/datatransfer/XMimeContentTypeFactory.hpp>
+#include <com/sun/star/datatransfer/XMimeContentType.hpp>
+
+#include "DataFlavorMapping.hxx"
+
+#include <premac.h>
+#import <Cocoa/Cocoa.h>
+#include <postmac.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include <vector>
+
+
+class OSXTransferable : public ::cppu::WeakImplHelper1<com::sun::star::datatransfer::XTransferable>,
+ private ::boost::noncopyable
+{
+public:
+ typedef com::sun::star::uno::Sequence< sal_Int8 > ByteSequence_t;
+
+ explicit OSXTransferable(com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XMimeContentTypeFactory> rXMimeCntFactory,
+ DataFlavorMapperPtr_t pDataFlavorMapper,
+ NSPasteboard* pasteboard);
+
+ virtual ~OSXTransferable();
+
+ //------------------------------------------------------------------------
+ // XTransferable
+ //------------------------------------------------------------------------
+
+ virtual ::com::sun::star::uno::Any SAL_CALL getTransferData( const ::com::sun::star::datatransfer::DataFlavor& aFlavor )
+ throw( ::com::sun::star::datatransfer::UnsupportedFlavorException, ::com::sun::star::io::IOException, ::com::sun::star::uno::RuntimeException );
+
+ virtual ::com::sun::star::uno::Sequence< ::com::sun::star::datatransfer::DataFlavor > SAL_CALL getTransferDataFlavors( )
+ throw( ::com::sun::star::uno::RuntimeException );
+
+ virtual sal_Bool SAL_CALL isDataFlavorSupported( const ::com::sun::star::datatransfer::DataFlavor& aFlavor )
+ throw( ::com::sun::star::uno::RuntimeException );
+
+ //------------------------------------------------------------------------
+ // Helper functions not part of the XTransferable interface
+ //------------------------------------------------------------------------
+
+ void initClipboardItemList();
+
+ //com::sun::star::uno::Any getClipboardItemData(ClipboardItemPtr_t clipboardItem);
+
+ bool isUnicodeText(const com::sun::star::datatransfer::DataFlavor& flavor);
+
+ bool compareDataFlavors( const com::sun::star::datatransfer::DataFlavor& lhs,
+ const com::sun::star::datatransfer::DataFlavor& rhs );
+
+ bool cmpAllContentTypeParameter( const com::sun::star::uno::Reference< com::sun::star::datatransfer::XMimeContentType > xLhs,
+ const com::sun::star::uno::Reference< com::sun::star::datatransfer::XMimeContentType > xRhs ) const;
+
+private:
+ com::sun::star::uno::Sequence< com::sun::star::datatransfer::DataFlavor > mFlavorList;
+ ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XMimeContentTypeFactory> mrXMimeCntFactory;
+ DataFlavorMapperPtr_t mDataFlavorMapper;
+ NSPasteboard* mPasteboard;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/PictToBmpFlt.cxx b/vcl/osx/PictToBmpFlt.cxx
new file mode 100644
index 000000000000..91ed2bc5f177
--- /dev/null
+++ b/vcl/osx/PictToBmpFlt.cxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <premac.h>
+#include <Carbon/Carbon.h>
+#include <QuickTime/QuickTime.h>
+#include <postmac.h>
+
+#include <string.h>
+
+#include "PictToBmpFlt.hxx"
+
+bool ImageToPNG( com::sun::star::uno::Sequence<sal_Int8>& rImgData,
+ com::sun::star::uno::Sequence<sal_Int8>& rPngData,
+ NSBitmapImageFileType eInFormat)
+{
+ (void) eInFormat; // Really not needed? Weird.
+
+ NSData* pData = [NSData dataWithBytesNoCopy: (void*)rImgData.getConstArray() length: rImgData.getLength() freeWhenDone: 0];
+ if( !pData)
+ return false;
+
+ NSBitmapImageRep* pRep =[NSBitmapImageRep imageRepWithData: pData];
+ if( !pRep)
+ return false;
+
+ NSData* pOut = [pRep representationUsingType: NSPNGFileType properties: nil];
+ if( !pOut)
+ return false;
+
+ const size_t nPngSize = [pOut length];
+ rPngData.realloc( nPngSize);
+ [pOut getBytes: rPngData.getArray() length: nPngSize];
+ return (nPngSize > 0);
+}
+
+bool PNGToImage( com::sun::star::uno::Sequence<sal_Int8>& rPngData,
+ com::sun::star::uno::Sequence<sal_Int8>& rImgData,
+ NSBitmapImageFileType eOutFormat
+ )
+{
+ NSData* pData = [NSData dataWithBytesNoCopy: const_cast<sal_Int8*>(rPngData.getConstArray()) length: rPngData.getLength() freeWhenDone: 0];
+ if( !pData)
+ return false;
+
+ NSBitmapImageRep* pRep = [NSBitmapImageRep imageRepWithData: pData];
+ if( !pRep)
+ return false;
+
+ NSData* pOut = [pRep representationUsingType: eOutFormat properties: nil];
+ if( !pOut)
+ return false;
+
+ const size_t nImgSize = [pOut length];
+ rImgData.realloc( nImgSize);
+ [pOut getBytes: rImgData.getArray() length: nImgSize];
+ return (nImgSize > 0);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/PictToBmpFlt.hxx b/vcl/osx/PictToBmpFlt.hxx
new file mode 100644
index 000000000000..59f1f2d3fa99
--- /dev/null
+++ b/vcl/osx/PictToBmpFlt.hxx
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_PICTTOBMPFLT_HXX
+#define INCLUDED_VCL_OSX_PICTTOBMPFLT_HXX
+
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <premac.h>
+#include <Cocoa/Cocoa.h>
+#include <postmac.h>
+
+bool ImageToPNG( com::sun::star::uno::Sequence<sal_Int8>& rImgData,
+ com::sun::star::uno::Sequence<sal_Int8>& rPngData,
+ NSBitmapImageFileType eInFormat);
+
+bool PNGToImage( com::sun::star::uno::Sequence<sal_Int8>& rPngData,
+ com::sun::star::uno::Sequence<sal_Int8>& rImgData,
+ NSBitmapImageFileType eOutFormat);
+
+#endif // INCLUDED_VCL_OSX_PICTTOBMPFLT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/README.a11y b/vcl/osx/README.a11y
new file mode 100644
index 000000000000..4422713bc4fa
--- /dev/null
+++ b/vcl/osx/README.a11y
@@ -0,0 +1,7 @@
+Naming scheme:
+
+a11yXYZhelper: Helper class providing static methods
+
+a11yXYZwrapper: Wrapper around one (or two) UNO-interfaces
+
+a11ywrapperXYZ: Subclass of a11ywrapper for a specific AXRole
diff --git a/vcl/osx/a11yactionwrapper.h b/vcl/osx/a11yactionwrapper.h
new file mode 100644
index 000000000000..0fda9a32ae21
--- /dev/null
+++ b/vcl/osx/a11yactionwrapper.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YACTIONWRAPPER_H
+#define INCLUDED_VCL_OSX_A11YACTIONWRAPPER_H
+
+#include "osx/osxvcltypes.h"
+#include "osx/a11ywrapper.h"
+
+@interface AquaA11yActionWrapper : NSObject
+{
+}
++(NSArray *)actionNamesForElement:(AquaA11yWrapper *)wrapper;
++(void)doAction:(NSString *)action ofElement:(AquaA11yWrapper *)wrapper;
+@end
+
+#endif // INCLUDED_VCL_OSX_A11YACTIONWRAPPER_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11yactionwrapper.mm b/vcl/osx/a11yactionwrapper.mm
new file mode 100644
index 000000000000..31dc39527ecd
--- /dev/null
+++ b/vcl/osx/a11yactionwrapper.mm
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/salinst.h"
+#include "quartz/utils.h"
+
+#include "a11yactionwrapper.h"
+
+// Wrapper for XAccessibleAction
+
+@implementation AquaA11yActionWrapper : NSObject
+
++(NSString *)nativeActionNameFor:(NSString *)actionName {
+ // TODO: Optimize ?
+ // Use NSAccessibilityActionDescription
+ if ( [ actionName isEqualToString: @"click" ] ) {
+ return NSAccessibilityPressAction;
+ } else if ( [ actionName isEqualToString: @"togglePopup" ] ) {
+ return NSAccessibilityShowMenuAction;
+ } else if ( [ actionName isEqualToString: @"select" ] ) {
+ return NSAccessibilityPickAction;
+ } else if ( [ actionName isEqualToString: @"incrementLine" ] ) {
+ return NSAccessibilityIncrementAction;
+ } else if ( [ actionName isEqualToString: @"decrementLine" ] ) {
+ return NSAccessibilityDecrementAction;
+ } else if ( [ actionName isEqualToString: @"incrementBlock" ] ) {
+ return NSAccessibilityIncrementAction; // TODO ?
+ } else if ( [ actionName isEqualToString: @"decrementBlock" ] ) {
+ return NSAccessibilityDecrementAction; // TODO ?
+ } else if ( [ actionName isEqualToString: @"Browse" ] ) {
+ return NSAccessibilityPressAction; // TODO ?
+ } else {
+ return [ NSString string ];
+ }
+}
+
++(NSArray *)actionNamesForElement:(AquaA11yWrapper *)wrapper {
+ NSMutableArray * actionNames = [ [ NSMutableArray alloc ] init ];
+ if ( [ wrapper accessibleAction ] != nil ) {
+ for ( int cnt = 0; cnt < [ wrapper accessibleAction ] -> getAccessibleActionCount(); cnt++ ) {
+ [ actionNames addObject: [ AquaA11yActionWrapper nativeActionNameFor: CreateNSString ( [ wrapper accessibleAction ] -> getAccessibleActionDescription ( cnt ) ) ] ];
+ }
+ }
+ return actionNames;
+}
+
++(void)doAction:(NSString *)action ofElement:(AquaA11yWrapper *)wrapper {
+ if ( [ wrapper accessibleAction ] != nil ) {
+ for ( int cnt = 0; cnt < [ wrapper accessibleAction ] -> getAccessibleActionCount(); cnt++ ) {
+ if ( [ action isEqualToString: [ AquaA11yActionWrapper nativeActionNameFor: CreateNSString ( [ wrapper accessibleAction ] -> getAccessibleActionDescription ( cnt ) ) ] ] ) {
+ [ wrapper accessibleAction ] -> doAccessibleAction ( cnt );
+ break;
+ }
+ }
+ }
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ycomponentwrapper.h b/vcl/osx/a11ycomponentwrapper.h
new file mode 100644
index 000000000000..450f0688155e
--- /dev/null
+++ b/vcl/osx/a11ycomponentwrapper.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YCOMPONENTWRAPPER_H
+#define INCLUDED_VCL_OSX_A11YCOMPONENTWRAPPER_H
+
+#include "osx/osxvcltypes.h"
+#include "osx/a11ywrapper.h"
+
+@interface AquaA11yComponentWrapper : NSObject
+{
+}
++(id)sizeAttributeForElement:(AquaA11yWrapper *)wrapper;
++(id)positionAttributeForElement:(AquaA11yWrapper *)wrapper;
++(id)descriptionAttributeForElement:(AquaA11yWrapper *)wrapper;
++(void)addAttributeNamesTo:(NSMutableArray *)attributeNames;
++(BOOL)isAttributeSettable:(NSString *)attribute forElement:(AquaA11yWrapper *)wrapper;
++(void)setFocusedAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value;
+@end
+
+#endif // INCLUDED_VCL_OSX_A11YCOMPONENTWRAPPER_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ycomponentwrapper.mm b/vcl/osx/a11ycomponentwrapper.mm
new file mode 100644
index 000000000000..b91029d6aca8
--- /dev/null
+++ b/vcl/osx/a11ycomponentwrapper.mm
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "quartz/utils.h"
+#include "a11ycomponentwrapper.h"
+#include "a11yrolehelper.h"
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::uno;
+
+// Wrapper for XAccessibleComponent and XAccessibleExtendedComponent
+
+@implementation AquaA11yComponentWrapper : NSObject
+
++(id)sizeAttributeForElement:(AquaA11yWrapper *)wrapper {
+ Size size = [ wrapper accessibleComponent ] -> getSize();
+ NSSize nsSize = NSMakeSize ( (float) size.Width, (float) size.Height );
+ return [ NSValue valueWithSize: nsSize ];
+}
+
+// TODO: should be merged with AquaSalFrame::VCLToCocoa... to a general helper method
++(id)positionAttributeForElement:(AquaA11yWrapper *)wrapper {
+ // VCL coordinates are in upper-left-notation, Cocoa likes it the Cartesian way (lower-left)
+ NSRect screenRect = [ [ NSScreen mainScreen ] frame ];
+ Size size = [ wrapper accessibleComponent ] -> getSize();
+ Point location = [ wrapper accessibleComponent ] -> getLocationOnScreen();
+ NSPoint nsPoint = NSMakePoint ( (float) location.X, (float) ( screenRect.size.height - size.Height - location.Y ) );
+ return [ NSValue valueWithPoint: nsPoint ];
+}
+
++(id)descriptionAttributeForElement:(AquaA11yWrapper *)wrapper {
+ if ( [ wrapper accessibleExtendedComponent ] != nil ) {
+ return CreateNSString ( [ wrapper accessibleExtendedComponent ] -> getToolTipText() );
+ } else {
+ return nil;
+ }
+}
+
++(void)addAttributeNamesTo:(NSMutableArray *)attributeNames {
+ NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
+ [ attributeNames addObjectsFromArray: [ NSArray arrayWithObjects:
+ NSAccessibilitySizeAttribute,
+ NSAccessibilityPositionAttribute,
+ NSAccessibilityFocusedAttribute,
+ NSAccessibilityEnabledAttribute,
+ nil ] ];
+ [ pool release ];
+}
+
++(BOOL)isAttributeSettable:(NSString *)attribute forElement:(AquaA11yWrapper *)wrapper {
+ BOOL isSettable = NO;
+ NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
+ if ( [ attribute isEqualToString: NSAccessibilityFocusedAttribute ]
+ && ! [ [ AquaA11yRoleHelper getNativeRoleFrom: [ wrapper accessibleContext ] ] isEqualToString: NSAccessibilityScrollBarRole ]
+ && ! [ [ AquaA11yRoleHelper getNativeRoleFrom: [ wrapper accessibleContext ] ] isEqualToString: NSAccessibilityStaticTextRole ] ) {
+ isSettable = YES;
+ }
+ [ pool release ];
+ return isSettable;
+}
+
++(void)setFocusedAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value {
+ if ( [ value boolValue ] == YES ) {
+ if ( [ wrapper accessibleContext ] -> getAccessibleRole() == AccessibleRole::COMBO_BOX ) {
+ // special treatment for comboboxes: find the corresponding PANEL and set focus to it
+ Reference < XAccessible > rxParent = [ wrapper accessibleContext ] -> getAccessibleParent();
+ if ( rxParent.is() ) {
+ Reference < XAccessibleContext > rxContext = rxParent->getAccessibleContext();
+ if ( rxContext.is() && rxContext -> getAccessibleRole() == AccessibleRole::PANEL ) {
+ Reference < XAccessibleComponent > rxComponent = Reference < XAccessibleComponent > ( rxParent -> getAccessibleContext(), UNO_QUERY );
+ if ( rxComponent.is() ) {
+ rxComponent -> grabFocus();
+ }
+ }
+ }
+ } else {
+ [ wrapper accessibleComponent ] -> grabFocus();
+ }
+ }
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11yfactory.mm b/vcl/osx/a11yfactory.mm
new file mode 100644
index 000000000000..30437a837936
--- /dev/null
+++ b/vcl/osx/a11yfactory.mm
@@ -0,0 +1,219 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/salinst.h"
+#include "osx/a11yfactory.h"
+#include "osx/a11yfocustracker.hxx"
+
+#include "a11yfocuslistener.hxx"
+#include "a11yrolehelper.h"
+#include "a11ywrapperbutton.h"
+#include "a11ywrapperstatictext.h"
+#include "a11ywrappertextarea.h"
+#include "a11ywrappercheckbox.h"
+#include "a11ywrappercombobox.h"
+#include "a11ywrappergroup.h"
+#include "a11ywrapperlist.h"
+#include "a11ywrapperradiobutton.h"
+#include "a11ywrapperradiogroup.h"
+#include "a11ywrapperrow.h"
+#include "a11ywrapperscrollarea.h"
+#include "a11ywrapperscrollbar.h"
+#include "a11ywrappersplitter.h"
+#include "a11ywrappertabgroup.h"
+#include "a11ywrappertoolbar.h"
+#include "a11ytablewrapper.h"
+
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::uno;
+
+static bool enabled = false;
+
+@implementation AquaA11yFactory : NSObject
+
+#pragma mark -
+#pragma mark Wrapper Repository
+
++(NSMutableDictionary *)allWrapper {
+ static NSMutableDictionary * mdAllWrapper = nil;
+ if ( mdAllWrapper == nil ) {
+ mdAllWrapper = [ [ [ NSMutableDictionary alloc ] init ] retain ];
+ // initialize keyboard focus tracker
+ rtl::Reference< AquaA11yFocusListener > listener( AquaA11yFocusListener::get() );
+ AquaA11yFocusTracker::get().setFocusListener(listener.get());
+ enabled = true;
+ }
+ return mdAllWrapper;
+}
+
++(NSValue *)keyForAccessibleContext: (Reference < XAccessibleContext >) rxAccessibleContext {
+ return [ NSValue valueWithPointer: rxAccessibleContext.get() ];
+}
+
++(NSValue *)keyForAccessibleContextAsRadioGroup: (Reference < XAccessibleContext >) rxAccessibleContext {
+ return [ NSValue valueWithPointer: ( rxAccessibleContext.get() + 2 ) ];
+}
+
++(AquaA11yWrapper *)wrapperForAccessible: (Reference < XAccessible >) rxAccessible {
+ if ( rxAccessible.is() ) {
+ Reference< XAccessibleContext > xAccessibleContext = rxAccessible->getAccessibleContext();
+ if( xAccessibleContext.is() ) {
+ return [ AquaA11yFactory wrapperForAccessibleContext: xAccessibleContext ];
+ }
+ }
+ return nil;
+}
+
++(AquaA11yWrapper *)wrapperForAccessibleContext: (Reference < XAccessibleContext >) rxAccessibleContext {
+ return [ AquaA11yFactory wrapperForAccessibleContext: rxAccessibleContext createIfNotExists: YES asRadioGroup: NO ];
+}
+
++(AquaA11yWrapper *)wrapperForAccessibleContext: (Reference < XAccessibleContext >) rxAccessibleContext createIfNotExists:(BOOL) bCreate {
+ return [ AquaA11yFactory wrapperForAccessibleContext: rxAccessibleContext createIfNotExists: bCreate asRadioGroup: NO ];
+}
+
++(AquaA11yWrapper *)wrapperForAccessibleContext: (Reference < XAccessibleContext >) rxAccessibleContext createIfNotExists:(BOOL) bCreate asRadioGroup:(BOOL) asRadioGroup{
+ NSMutableDictionary * dAllWrapper = [ AquaA11yFactory allWrapper ];
+ NSValue * nKey = nil;
+ if ( asRadioGroup ) {
+ nKey = [ AquaA11yFactory keyForAccessibleContextAsRadioGroup: rxAccessibleContext ];
+ } else {
+ nKey = [ AquaA11yFactory keyForAccessibleContext: rxAccessibleContext ];
+ }
+ AquaA11yWrapper * aWrapper = (AquaA11yWrapper *) [ dAllWrapper objectForKey: nKey ];
+ if ( aWrapper != nil ) {
+ [ aWrapper retain ];
+ } else if ( bCreate ) {
+ NSString * nativeRole = [ AquaA11yRoleHelper getNativeRoleFrom: rxAccessibleContext.get() ];
+ // TODO: reflection
+ if ( [ nativeRole isEqualToString: NSAccessibilityButtonRole ] ) {
+ aWrapper = [ [ AquaA11yWrapperButton alloc ] initWithAccessibleContext: rxAccessibleContext ];
+ } else if ( [ nativeRole isEqualToString: NSAccessibilityTextAreaRole ] ) {
+ aWrapper = [ [ AquaA11yWrapperTextArea alloc ] initWithAccessibleContext: rxAccessibleContext ];
+ } else if ( [ nativeRole isEqualToString: NSAccessibilityStaticTextRole ] ) {
+ aWrapper = [ [ AquaA11yWrapperStaticText alloc ] initWithAccessibleContext: rxAccessibleContext ];
+ } else if ( [ nativeRole isEqualToString: NSAccessibilityComboBoxRole ] ) {
+ aWrapper = [ [ AquaA11yWrapperComboBox alloc ] initWithAccessibleContext: rxAccessibleContext ];
+ } else if ( [ nativeRole isEqualToString: NSAccessibilityGroupRole ] ) {
+ aWrapper = [ [ AquaA11yWrapperGroup alloc ] initWithAccessibleContext: rxAccessibleContext ];
+ } else if ( [ nativeRole isEqualToString: NSAccessibilityToolbarRole ] ) {
+ aWrapper = [ [ AquaA11yWrapperToolbar alloc ] initWithAccessibleContext: rxAccessibleContext ];
+ } else if ( [ nativeRole isEqualToString: NSAccessibilityScrollAreaRole ] ) {
+ aWrapper = [ [ AquaA11yWrapperScrollArea alloc ] initWithAccessibleContext: rxAccessibleContext ];
+ } else if ( [ nativeRole isEqualToString: NSAccessibilityTabGroupRole ] ) {
+ aWrapper = [ [ AquaA11yWrapperTabGroup alloc ] initWithAccessibleContext: rxAccessibleContext ];
+ } else if ( [ nativeRole isEqualToString: NSAccessibilityScrollBarRole ] ) {
+ aWrapper = [ [ AquaA11yWrapperScrollBar alloc ] initWithAccessibleContext: rxAccessibleContext ];
+ } else if ( [ nativeRole isEqualToString: NSAccessibilityCheckBoxRole ] ) {
+ aWrapper = [ [ AquaA11yWrapperCheckBox alloc ] initWithAccessibleContext: rxAccessibleContext ];
+ } else if ( [ nativeRole isEqualToString: NSAccessibilityRadioGroupRole ] ) {
+ aWrapper = [ [ AquaA11yWrapperRadioGroup alloc ] initWithAccessibleContext: rxAccessibleContext ];
+ } else if ( [ nativeRole isEqualToString: NSAccessibilityRadioButtonRole ] ) {
+ aWrapper = [ [ AquaA11yWrapperRadioButton alloc ] initWithAccessibleContext: rxAccessibleContext ];
+ } else if ( [ nativeRole isEqualToString: NSAccessibilityRowRole ] ) {
+ aWrapper = [ [ AquaA11yWrapperRow alloc ] initWithAccessibleContext: rxAccessibleContext ];
+ } else if ( [ nativeRole isEqualToString: NSAccessibilityListRole ] ) {
+ aWrapper = [ [ AquaA11yWrapperList alloc ] initWithAccessibleContext: rxAccessibleContext ];
+ } else if ( [ nativeRole isEqualToString: NSAccessibilitySplitterRole ] ) {
+ aWrapper = [ [ AquaA11yWrapperSplitter alloc ] initWithAccessibleContext: rxAccessibleContext ];
+ } else if ( [ nativeRole isEqualToString: NSAccessibilityTableRole ] ) {
+ aWrapper = [ [ AquaA11yTableWrapper alloc ] initWithAccessibleContext: rxAccessibleContext ];
+ } else {
+ aWrapper = [ [ AquaA11yWrapper alloc ] initWithAccessibleContext: rxAccessibleContext ];
+ }
+ [ nativeRole release ];
+ [ aWrapper setActsAsRadioGroup: asRadioGroup ];
+ #if 0
+ /* #i102033# NSAccessibility does not seemt to know an equivalent for transient children.
+ That means we need to cache this, else e.g. tree list boxes are not accessible (moreover
+ it crashes by notifying dead objects - which would seemt o be another bug)
+
+ FIXME:
+ Unfortunately this can increase memory consumption drastically until the non transient parent
+ is destroyed an finally all the transients are released.
+ */
+ if ( ! rxAccessibleContext -> getAccessibleStateSet() -> contains ( AccessibleStateType::TRANSIENT ) )
+ #endif
+ {
+ [ dAllWrapper setObject: aWrapper forKey: nKey ];
+ /* fdo#67410: Accessibility notifications are not delivered on NSView subclasses that do not
+ "reasonably" participate in NSView hierarchy (perhaps the only important point is
+ that the view is a transitive subview of the NSWindow's content view, but I
+ did not try to verify that).
+
+ So let the superview-subviews relationship mirror the AXParent-AXChildren relationship.
+ */
+ id parent = [aWrapper accessibilityAttributeValue:NSAccessibilityParentAttribute];
+ if (parent) {
+ if ([parent isKindOfClass:[NSView class]]) {
+ // SAL_DEBUG("Wrapper INIT: " << [[aWrapper description] UTF8String] << " ==> " << [[parent description] UTF8String]);
+ NSView *parentView = (NSView *)parent;
+ [parentView addSubview:aWrapper positioned:NSWindowBelow relativeTo:nil];
+ } else if ([parent isKindOfClass:NSClassFromString(@"SalFrameWindow")]) {
+ NSWindow *window = (NSWindow *)parent;
+ NSView *salView = [window contentView];
+ // SAL_DEBUG("Wrapper INIT SAL: " << [[aWrapper description] UTF8String] << " ==> " << [[salView description] UTF8String]);
+ [salView addSubview:aWrapper positioned:NSWindowBelow relativeTo:nil];
+ } else {
+ // SAL_DEBUG("Wrapper INIT: !! " << [[aWrapper description] UTF8String] << " !==>! " << [[parent description] UTF8String] << "!!");
+ }
+ } else {
+ // SAL_DEBUG("Wrapper INIT: " << [[aWrapper description] UTF8String] << " ==> NO PARENT");
+ }
+ }
+ }
+ return aWrapper;
+}
+
++(void)insertIntoWrapperRepository: (NSView *) viewElement forAccessibleContext: (Reference < XAccessibleContext >) rxAccessibleContext {
+ NSMutableDictionary * dAllWrapper = [ AquaA11yFactory allWrapper ];
+ [ dAllWrapper setObject: viewElement forKey: [ AquaA11yFactory keyForAccessibleContext: rxAccessibleContext ] ];
+}
+
++(void)removeFromWrapperRepositoryFor: (::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleContext >) rxAccessibleContext {
+ // TODO: when RADIO_BUTTON search for associated RadioGroup-wrapper and delete that as well
+ AquaA11yWrapper * theWrapper = [ AquaA11yFactory wrapperForAccessibleContext: rxAccessibleContext createIfNotExists: NO ];
+ if ( theWrapper != nil ) {
+ if (![theWrapper isKindOfClass:NSClassFromString(@"SalFrameView")]) {
+ [theWrapper removeFromSuperview];
+ }
+ [ [ AquaA11yFactory allWrapper ] removeObjectForKey: [ AquaA11yFactory keyForAccessibleContext: rxAccessibleContext ] ];
+ [ theWrapper release ];
+ }
+}
+
++(void)registerView: (NSView *) theView {
+ if ( enabled && [ theView isKindOfClass: [ AquaA11yWrapper class ] ] ) {
+ // insertIntoWrapperRepository gets called from SalFrameView itself to bootstrap the bridge initially
+ [ (AquaA11yWrapper *) theView accessibleContext ];
+ }
+}
+
++(void)revokeView: (NSView *) theView {
+ if ( enabled && [ theView isKindOfClass: [ AquaA11yWrapper class ] ] ) {
+ [ AquaA11yFactory removeFromWrapperRepositoryFor: [ (AquaA11yWrapper *) theView accessibleContext ] ];
+ }
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11yfocuslistener.cxx b/vcl/osx/a11yfocuslistener.cxx
new file mode 100644
index 000000000000..c4e296072868
--- /dev/null
+++ b/vcl/osx/a11yfocuslistener.cxx
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <salhelper/refobj.hxx>
+
+#include "osx/a11yfocustracker.hxx"
+#include "osx/a11yfactory.h"
+
+#include "a11yfocuslistener.hxx"
+
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::uno;
+
+
+rtl::Reference< AquaA11yFocusListener > AquaA11yFocusListener::theListener;
+
+//------------------------------------------------------------------------------
+
+rtl::Reference< AquaA11yFocusListener > AquaA11yFocusListener::get()
+{
+ if ( ! theListener.is() )
+ theListener = new AquaA11yFocusListener();
+
+ return theListener;
+}
+
+//------------------------------------------------------------------------------
+
+AquaA11yFocusListener::AquaA11yFocusListener() : m_focusedObject(nil)
+{
+}
+
+//------------------------------------------------------------------------------
+
+id AquaA11yFocusListener::getFocusedUIElement()
+{
+ if ( nil == m_focusedObject ) {
+ Reference< XAccessible > xAccessible( AquaA11yFocusTracker::get().getFocusedObject() );
+ try {
+ if( xAccessible.is() ) {
+ Reference< XAccessibleContext > xContext(xAccessible->getAccessibleContext());
+ if( xContext.is() )
+ m_focusedObject = [ AquaA11yFactory wrapperForAccessibleContext: xContext ];
+ }
+ } catch(const RuntimeException &) {
+ // intentionally do nothing ..
+ }
+ }
+
+ return m_focusedObject;
+}
+
+//------------------------------------------------------------------------------
+
+void SAL_CALL
+AquaA11yFocusListener::focusedObjectChanged(const Reference< XAccessible >& xAccessible)
+{
+ if ( nil != m_focusedObject ) {
+ [ m_focusedObject release ];
+ m_focusedObject = nil;
+ }
+
+ try {
+ if( xAccessible.is() ) {
+ Reference< XAccessibleContext > xContext(xAccessible->getAccessibleContext());
+ if( xContext.is() )
+ {
+ m_focusedObject = [ AquaA11yFactory wrapperForAccessibleContext: xContext ];
+ NSAccessibilityPostNotification(m_focusedObject, NSAccessibilityFocusedUIElementChangedNotification);
+ }
+ }
+ } catch(const RuntimeException &) {
+ // intentionally do nothing ..
+ }
+}
+
+//------------------------------------------------------------------------------
+
+oslInterlockedCount SAL_CALL
+AquaA11yFocusListener::acquire() SAL_THROW(())
+{
+ return ReferenceObject::acquire();
+}
+
+//------------------------------------------------------------------------------
+
+oslInterlockedCount SAL_CALL
+AquaA11yFocusListener::release() SAL_THROW(())
+{
+ return ReferenceObject::release();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11yfocuslistener.hxx b/vcl/osx/a11yfocuslistener.hxx
new file mode 100644
index 000000000000..42d5c358b4cd
--- /dev/null
+++ b/vcl/osx/a11yfocuslistener.hxx
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YFOCUSLISTENER_HXX
+#define INCLUDED_VCL_OSX_A11YFOCUSLISTENER_HXX
+
+#include <salhelper/refobj.hxx>
+
+#include "osx/keyboardfocuslistener.hxx"
+#include "osx/osxvcltypes.h"
+
+class AquaA11yFocusListener :
+ public KeyboardFocusListener,
+ public salhelper::ReferenceObject
+{
+ id m_focusedObject;
+
+ static rtl::Reference< AquaA11yFocusListener > theListener;
+
+ AquaA11yFocusListener();
+ virtual ~AquaA11yFocusListener() {};
+public:
+
+ static rtl::Reference< AquaA11yFocusListener > get();
+
+ id getFocusedUIElement();
+
+ // KeyboardFocusListener
+ virtual void SAL_CALL focusedObjectChanged(const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& xAccessible);
+
+ // rtl::IReference
+ virtual oslInterlockedCount SAL_CALL acquire() SAL_THROW(());
+ virtual oslInterlockedCount SAL_CALL release() SAL_THROW(());
+};
+
+#endif // INCLUDED_VCL_OSX_A11YFOCUSLISTENER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11yfocustracker.cxx b/vcl/osx/a11yfocustracker.cxx
new file mode 100644
index 000000000000..1017969c879a
--- /dev/null
+++ b/vcl/osx/a11yfocustracker.cxx
@@ -0,0 +1,284 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "vcl/svapp.hxx"
+#include "vcl/window.hxx"
+#include "vcl/toolbox.hxx"
+#include "vcl/menu.hxx"
+
+#include "osx/a11yfocustracker.hxx"
+
+#include "documentfocuslistener.hxx"
+
+#include <com/sun/star/accessibility/XAccessibleContext.hpp>
+#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
+#include <com/sun/star/accessibility/XAccessibleStateSet.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::uno;
+
+//------------------------------------------------------------------------------
+
+static inline Window *
+getWindow(const ::VclSimpleEvent *pEvent)
+{
+ return static_cast< const ::VclWindowEvent *> (pEvent)->GetWindow();
+}
+
+
+//------------------------------------------------------------------------------
+
+// callback function for Application::addEventListener
+
+long AquaA11yFocusTracker::WindowEventHandler(AquaA11yFocusTracker *pFocusTracker, ::VclSimpleEvent const *pEvent)
+{
+ switch (pEvent->GetId())
+ {
+ case VCLEVENT_WINDOW_PAINT:
+ pFocusTracker-> toolbox_open_floater( getWindow(pEvent) );
+ break;
+ case VCLEVENT_WINDOW_GETFOCUS:
+ pFocusTracker->window_got_focus( getWindow(pEvent) );
+ break;
+ case VCLEVENT_OBJECT_DYING:
+ pFocusTracker->m_aDocumentWindowList.erase( getWindow(pEvent) );
+ // intentional pass through ..
+ case VCLEVENT_TOOLBOX_HIGHLIGHTOFF:
+ pFocusTracker->toolbox_highlight_off( getWindow(pEvent) );
+ break;
+ case VCLEVENT_TOOLBOX_HIGHLIGHT:
+ pFocusTracker->toolbox_highlight_on( getWindow(pEvent) );
+ break;
+ case VCLEVENT_TABPAGE_ACTIVATE:
+ pFocusTracker->tabpage_activated( getWindow(pEvent) );
+ break;
+ case VCLEVENT_MENU_HIGHLIGHT:
+ // Inspired by code in WindowEventHandler in
+ // vcl/unx/gtk/a11y/atkutil.cxx, find out what kind of event
+ // it is to avoid blindly using a static_cast and crash,
+ // fdo#47275.
+ if( const VclMenuEvent* pMenuEvent = dynamic_cast < const VclMenuEvent* > (pEvent) )
+ {
+ pFocusTracker->menu_highlighted( pMenuEvent );
+ }
+ else if( const VclAccessibleEvent* pAccEvent = dynamic_cast < const VclAccessibleEvent* > (pEvent) )
+ {
+ Reference< XAccessible > xAccessible = pAccEvent->GetAccessible();
+ if( xAccessible.is() )
+ pFocusTracker->setFocusedObject( xAccessible );
+ }
+ break;
+ default:
+ break;
+ };
+
+ return 0;
+}
+
+//------------------------------------------------------------------------------
+
+AquaA11yFocusTracker::AquaA11yFocusTracker() :
+ m_aWindowEventLink(this, (PSTUB) WindowEventHandler),
+ m_xDocumentFocusListener(new DocumentFocusListener(*this))
+{
+ Application::AddEventListener(m_aWindowEventLink);
+ window_got_focus(Application::GetFocusWindow());
+}
+
+//------------------------------------------------------------------------------
+
+void AquaA11yFocusTracker::setFocusedObject(const Reference< XAccessible >& xAccessible)
+{
+ if( xAccessible != m_xFocusedObject )
+ {
+ m_xFocusedObject = xAccessible;
+
+ if( m_aFocusListener.is() )
+ m_aFocusListener->focusedObjectChanged(xAccessible);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+void AquaA11yFocusTracker::notify_toolbox_item_focus(ToolBox *pToolBox)
+{
+ Reference< XAccessible > xAccessible( pToolBox->GetAccessible() );
+
+ if( xAccessible.is() )
+ {
+ Reference< XAccessibleContext > xContext(xAccessible->getAccessibleContext());
+
+ if( xContext.is() )
+ {
+ sal_Int32 nPos = pToolBox->GetItemPos( pToolBox->GetHighlightItemId() );
+ if( nPos != TOOLBOX_ITEM_NOTFOUND )
+ setFocusedObject( xContext->getAccessibleChild( nPos ) );
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+
+void AquaA11yFocusTracker::toolbox_open_floater(Window *pWindow)
+{
+ bool bToolboxFound = false;
+ bool bFloatingWindowFound = false;
+ Window * pFloatingWindow = NULL;
+ while ( pWindow != NULL ) {
+ if ( pWindow->GetType() == WINDOW_TOOLBOX ) {
+ bToolboxFound = true;
+ } else if ( pWindow->GetType() == WINDOW_FLOATINGWINDOW ) {
+ bFloatingWindowFound = true;
+ pFloatingWindow = pWindow;
+ }
+ pWindow = pWindow->GetParent();
+ }
+ if ( bToolboxFound && bFloatingWindowFound ) {
+ Reference < XAccessible > rxAccessible = pFloatingWindow -> GetAccessible();
+ if ( ! rxAccessible.is() ) {
+ return;
+ }
+ Reference < XAccessibleContext > rxContext = rxAccessible -> getAccessibleContext();
+ if ( ! rxContext.is() ) {
+ return;
+ }
+ if ( rxContext -> getAccessibleChildCount() > 0 ) {
+ Reference < XAccessible > rxAccessibleChild = rxContext -> getAccessibleChild( 0 );
+ if ( ! rxAccessibleChild.is() ) {
+ return;
+ }
+ setFocusedObject ( rxAccessibleChild );
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+
+void AquaA11yFocusTracker::toolbox_highlight_on(Window *pWindow)
+{
+ // Make sure either the toolbox or its parent toolbox has the focus
+ if ( ! pWindow->HasFocus() )
+ {
+ ToolBox* pToolBoxParent = dynamic_cast< ToolBox * >( pWindow->GetParent() );
+ if ( ! pToolBoxParent || ! pToolBoxParent->HasFocus() )
+ return;
+ }
+
+ notify_toolbox_item_focus(static_cast <ToolBox *> (pWindow));
+}
+
+//------------------------------------------------------------------------------
+
+void AquaA11yFocusTracker::toolbox_highlight_off(Window *pWindow)
+{
+ ToolBox* pToolBoxParent = dynamic_cast< ToolBox * >( pWindow->GetParent() );
+
+ // Notify when leaving sub toolboxes
+ if( pToolBoxParent && pToolBoxParent->HasFocus() )
+ notify_toolbox_item_focus( pToolBoxParent );
+}
+
+//------------------------------------------------------------------------------
+
+void AquaA11yFocusTracker::tabpage_activated(Window *pWindow)
+{
+ Reference< XAccessible > xAccessible( pWindow->GetAccessible() );
+
+ if( xAccessible.is() )
+ {
+ Reference< XAccessibleSelection > xSelection(xAccessible->getAccessibleContext(), UNO_QUERY);
+
+ if( xSelection.is() )
+ setFocusedObject( xSelection->getSelectedAccessibleChild(0) );
+ }
+}
+
+//------------------------------------------------------------------------------
+
+void AquaA11yFocusTracker::menu_highlighted(const VclMenuEvent *pEvent)
+{
+ Menu * pMenu = pEvent->GetMenu();
+
+ if( pMenu )
+ {
+ Reference< XAccessible > xAccessible( pMenu->GetAccessible() );
+
+ if( xAccessible.is() )
+ setFocusedObject( xAccessible );
+ }
+}
+
+//------------------------------------------------------------------------------
+
+void AquaA11yFocusTracker::window_got_focus(Window *pWindow)
+{
+ // The menu bar is handled through VCLEVENT_MENU_HIGHLIGHTED
+ if( ! pWindow || !pWindow->IsReallyVisible() || pWindow->GetType() == WINDOW_MENUBARWINDOW )
+ return;
+
+ // ToolBoxes are handled through VCLEVENT_TOOLBOX_HIGHLIGHT
+ if( pWindow->GetType() == WINDOW_TOOLBOX )
+ return;
+
+ if( pWindow->GetType() == WINDOW_TABCONTROL )
+ {
+ tabpage_activated( pWindow );
+ return;
+ }
+
+ Reference< XAccessible > xAccessible(pWindow->GetAccessible());
+
+ if( ! xAccessible.is() )
+ return;
+
+ Reference< XAccessibleContext > xContext = xAccessible->getAccessibleContext();
+
+ if( ! xContext.is() )
+ return;
+
+ Reference< XAccessibleStateSet > xStateSet = xContext->getAccessibleStateSet();
+
+ if( ! xStateSet.is() )
+ return;
+
+/* the UNO ToolBox wrapper does not (yet?) support XAccessibleSelection, so we
+ * need to add listeners to the children instead of re-using the tabpage stuff
+ */
+ if( xStateSet->contains(AccessibleStateType::FOCUSED) && (pWindow->GetType() != WINDOW_TREELISTBOX) )
+ {
+ setFocusedObject( xAccessible );
+ }
+ else
+ {
+ if( m_aDocumentWindowList.find(pWindow) == m_aDocumentWindowList.end() )
+ {
+ m_aDocumentWindowList.insert(pWindow);
+ m_xDocumentFocusListener->attachRecursive(xAccessible, xContext, xStateSet);
+ }
+#ifdef ENABLE_TRACING
+ else
+ fprintf(stderr, "Window %p already in the list\n", pWindow );
+#endif
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ylistener.cxx b/vcl/osx/a11ylistener.cxx
new file mode 100644
index 000000000000..dafabb7133c2
--- /dev/null
+++ b/vcl/osx/a11ylistener.cxx
@@ -0,0 +1,153 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "osx/salinst.h"
+#include "osx/a11ylistener.hxx"
+#include "osx/a11yfactory.h"
+#include "osx/a11yfocustracker.hxx"
+#include "osx/a11ywrapper.h"
+
+#include "a11ytextwrapper.h"
+
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleTableModelChange.hpp>
+#include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp>
+
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+
+NSString * getTableNotification( const AccessibleEventObject& aEvent )
+{
+ AccessibleTableModelChange aChange;
+ NSString * notification = nil;
+
+ if( (aEvent.NewValue >>= aChange) &&
+ ( AccessibleTableModelChangeType::INSERT == aChange.Type || AccessibleTableModelChangeType::DELETE == aChange.Type ) &&
+ aChange.FirstRow != aChange.LastRow )
+ {
+ notification = NSAccessibilityRowCountChangedNotification;
+ }
+
+ return notification;
+}
+
+//------------------------------------------------------------------------------
+
+AquaA11yEventListener::AquaA11yEventListener(id wrapperObject, sal_Int16 role) : m_wrapperObject(wrapperObject), m_role(role)
+{
+ [ m_wrapperObject retain ];
+}
+
+//------------------------------------------------------------------------------
+
+AquaA11yEventListener::~AquaA11yEventListener()
+{
+ [ m_wrapperObject release ];
+}
+
+//------------------------------------------------------------------------------
+
+void SAL_CALL
+AquaA11yEventListener::disposing( const EventObject& ) throw( RuntimeException )
+{
+ [ AquaA11yFactory removeFromWrapperRepositoryFor: [ (AquaA11yWrapper *) m_wrapperObject accessibleContext ] ];
+}
+
+//------------------------------------------------------------------------------
+
+void SAL_CALL
+AquaA11yEventListener::notifyEvent( const AccessibleEventObject& aEvent ) throw( RuntimeException )
+{
+ NSString * notification = nil;
+ id element = m_wrapperObject;
+ Rectangle bounds;
+
+ // TODO: NSAccessibilityValueChanged, NSAccessibilitySelectedRowsChangedNotification
+ switch( aEvent.EventId )
+ {
+ case AccessibleEventId::ACTIVE_DESCENDANT_CHANGED:
+ if( m_role != AccessibleRole::LIST ) {
+ Reference< XAccessible > xAccessible;
+ if( aEvent.NewValue >>= xAccessible )
+ AquaA11yFocusTracker::get().setFocusedObject( xAccessible );
+ }
+ break;
+
+ case AccessibleEventId::NAME_CHANGED:
+ notification = NSAccessibilityTitleChangedNotification;
+ break;
+
+ case AccessibleEventId::CHILD:
+ // only needed for tooltips (says Apple)
+ if ( m_role == AccessibleRole::TOOL_TIP ) {
+ if(aEvent.NewValue.hasValue()) {
+ notification = NSAccessibilityCreatedNotification;
+ } else if(aEvent.OldValue.hasValue()) {
+ notification = NSAccessibilityUIElementDestroyedNotification;
+ }
+ }
+ break;
+
+ case AccessibleEventId::INVALIDATE_ALL_CHILDREN:
+ // TODO: depricate or remember all children
+ break;
+
+ case AccessibleEventId::BOUNDRECT_CHANGED:
+ bounds = [ element accessibleComponent ] -> getBounds();
+ if ( m_oldBounds.X != 0 && ( bounds.X != m_oldBounds.X || bounds.Y != m_oldBounds.Y ) ) {
+ NSAccessibilityPostNotification(element, NSAccessibilityMovedNotification); // post directly since both cases can happen simultaneously
+ }
+ if ( m_oldBounds.X != 0 && ( bounds.Width != m_oldBounds.Width || bounds.Height != m_oldBounds.Height ) ) {
+ NSAccessibilityPostNotification(element, NSAccessibilityResizedNotification); // post directly since both cases can happen simultaneously
+ }
+ m_oldBounds = bounds;
+ break;
+
+ case AccessibleEventId::SELECTION_CHANGED:
+ notification = NSAccessibilitySelectedChildrenChangedNotification;
+ break;
+
+ case AccessibleEventId::TEXT_SELECTION_CHANGED:
+ notification = NSAccessibilitySelectedTextChangedNotification;
+ break;
+
+ case AccessibleEventId::TABLE_MODEL_CHANGED:
+ notification = getTableNotification(aEvent);
+ break;
+
+ case AccessibleEventId::CARET_CHANGED:
+ notification = NSAccessibilitySelectedTextChangedNotification;
+ break;
+
+ case AccessibleEventId::TEXT_CHANGED:
+ notification = NSAccessibilityValueChangedNotification;
+ break;
+
+ default:
+ break;
+ }
+
+ if( nil != notification )
+ NSAccessibilityPostNotification(element, notification);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11yrolehelper.h b/vcl/osx/a11yrolehelper.h
new file mode 100644
index 000000000000..21b2ff5b960a
--- /dev/null
+++ b/vcl/osx/a11yrolehelper.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YROLEHELPER_H
+#define INCLUDED_VCL_OSX_A11YROLEHELPER_H
+
+#include "osx/salinst.h"
+#include <com/sun/star/accessibility/XAccessibleContext.hpp>
+
+@interface AquaA11yRoleHelper : NSObject
+{
+}
++(id)getNativeRoleFrom: (::com::sun::star::accessibility::XAccessibleContext *) accessibleContext;
++(id)getNativeSubroleFrom: (sal_Int16) nRole;
++(id)getRoleDescriptionFrom: (NSString *) role with: (NSString *) subRole;
+@end
+
+#endif // INCLUDED_VCL_OSX_A11YROLEHELPER_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11yrolehelper.mm b/vcl/osx/a11yrolehelper.mm
new file mode 100644
index 000000000000..2940a4b2f7aa
--- /dev/null
+++ b/vcl/osx/a11yrolehelper.mm
@@ -0,0 +1,270 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/a11yfactory.h"
+
+#include "a11yrolehelper.h"
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::uno;
+
+@implementation AquaA11yRoleHelper
+
++(id)simpleMapNativeRoleFrom: (XAccessibleContext *) accessibleContext {
+ id nativeRole = nil;
+
+ if (accessibleContext == NULL)
+ return nativeRole;
+
+ switch( accessibleContext -> getAccessibleRole() ) {
+#define MAP(a,b) \
+ case a: nativeRole = b; break
+
+ MAP( AccessibleRole::UNKNOWN, NSAccessibilityUnknownRole );
+ MAP( AccessibleRole::ALERT, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::COLUMN_HEADER, NSAccessibilityColumnRole );
+ MAP( AccessibleRole::CANVAS, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::CHECK_BOX, NSAccessibilityCheckBoxRole );
+ MAP( AccessibleRole::CHECK_MENU_ITEM, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::COLOR_CHOOSER, NSAccessibilityColorWellRole ); // FIXME
+ MAP( AccessibleRole::COMBO_BOX, NSAccessibilityComboBoxRole );
+ MAP( AccessibleRole::DATE_EDITOR, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::DESKTOP_ICON, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::DESKTOP_PANE, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::DIRECTORY_PANE, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::DIALOG, NSAccessibilityGroupRole );
+ MAP( AccessibleRole::DOCUMENT, NSAccessibilityGroupRole );
+ MAP( AccessibleRole::EMBEDDED_OBJECT, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::END_NOTE, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::FILE_CHOOSER, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::FILLER, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::FONT_CHOOSER, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::FOOTER, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::FOOTNOTE, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::FRAME, NSAccessibilityWindowRole );
+ MAP( AccessibleRole::GLASS_PANE, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::GRAPHIC, NSAccessibilityImageRole );
+ MAP( AccessibleRole::GROUP_BOX, NSAccessibilityGroupRole );
+ MAP( AccessibleRole::HEADER, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::HEADING, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::HYPER_LINK, NSAccessibilityLinkRole );
+ MAP( AccessibleRole::ICON, NSAccessibilityImageRole );
+ MAP( AccessibleRole::INTERNAL_FRAME, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::LABEL, NSAccessibilityStaticTextRole );
+ MAP( AccessibleRole::LAYERED_PANE, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::LIST, NSAccessibilityMenuRole );
+ MAP( AccessibleRole::LIST_ITEM, NSAccessibilityMenuItemRole );
+ MAP( AccessibleRole::MENU, NSAccessibilityMenuRole );
+ MAP( AccessibleRole::MENU_BAR, NSAccessibilityMenuBarRole );
+ MAP( AccessibleRole::MENU_ITEM, NSAccessibilityMenuItemRole );
+ MAP( AccessibleRole::OPTION_PANE, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::PAGE_TAB, NSAccessibilityButtonRole );
+ MAP( AccessibleRole::PAGE_TAB_LIST, NSAccessibilityTabGroupRole );
+ MAP( AccessibleRole::PANEL, NSAccessibilityGroupRole );
+ MAP( AccessibleRole::PARAGRAPH, NSAccessibilityTextAreaRole );
+ MAP( AccessibleRole::PASSWORD_TEXT, NSAccessibilityTextFieldRole );
+ MAP( AccessibleRole::POPUP_MENU, NSAccessibilityMenuRole );
+ MAP( AccessibleRole::PUSH_BUTTON, NSAccessibilityButtonRole );
+ MAP( AccessibleRole::PROGRESS_BAR, NSAccessibilityProgressIndicatorRole );
+ MAP( AccessibleRole::RADIO_BUTTON, NSAccessibilityRadioButtonRole );
+ MAP( AccessibleRole::RADIO_MENU_ITEM, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::ROW_HEADER, NSAccessibilityRowRole );
+ MAP( AccessibleRole::ROOT_PANE, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::SCROLL_BAR, NSAccessibilityScrollBarRole );
+ MAP( AccessibleRole::SCROLL_PANE, NSAccessibilityScrollAreaRole );
+ MAP( AccessibleRole::SHAPE, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::SEPARATOR, NSAccessibilitySplitterRole ); // FIXME
+ MAP( AccessibleRole::SLIDER, NSAccessibilitySliderRole );
+ MAP( AccessibleRole::SPIN_BOX, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::SPLIT_PANE, NSAccessibilitySplitterRole );
+ MAP( AccessibleRole::STATUS_BAR, NSAccessibilityGroupRole ); // FIXME
+ MAP( AccessibleRole::TABLE, NSAccessibilityTableRole );
+ MAP( AccessibleRole::TABLE_CELL, NSAccessibilityTextFieldRole );
+ MAP( AccessibleRole::TEXT, NSAccessibilityTextAreaRole );
+ MAP( AccessibleRole::TEXT_FRAME, NSAccessibilityGroupRole );
+ MAP( AccessibleRole::TOGGLE_BUTTON, NSAccessibilityCheckBoxRole );
+ MAP( AccessibleRole::TOOL_BAR, NSAccessibilityToolbarRole );
+ MAP( AccessibleRole::TOOL_TIP, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::TREE, NSAccessibilityGroupRole );
+ MAP( AccessibleRole::VIEW_PORT, NSAccessibilityUnknownRole ); // FIXME
+ MAP( AccessibleRole::WINDOW, NSAccessibilityWindowRole );
+
+ MAP( AccessibleRole::BUTTON_DROPDOWN, NSAccessibilityMenuButtonRole );
+ MAP( AccessibleRole::BUTTON_MENU, NSAccessibilityMenuButtonRole );
+ MAP( AccessibleRole::CAPTION, NSAccessibilityUnknownRole );
+ MAP( AccessibleRole::CHART, NSAccessibilityUnknownRole );
+ MAP( AccessibleRole::FORM, NSAccessibilityUnknownRole );
+ MAP( AccessibleRole::IMAGE_MAP, NSAccessibilityUnknownRole );
+ MAP( AccessibleRole::NOTE, NSAccessibilityUnknownRole );
+ MAP( AccessibleRole::PAGE, NSAccessibilityUnknownRole );
+ MAP( AccessibleRole::RULER, NSAccessibilityUnknownRole );
+ MAP( AccessibleRole::SECTION, NSAccessibilityUnknownRole );
+ MAP( AccessibleRole::TREE_ITEM, NSAccessibilityUnknownRole );
+ MAP( AccessibleRole::TREE_TABLE, NSAccessibilityUnknownRole );
+
+#undef MAP
+ default:
+ break;
+ }
+ return nativeRole;
+}
+
++(id)getNativeRoleFrom: (XAccessibleContext *) accessibleContext {
+ id nativeRole = [ AquaA11yRoleHelper simpleMapNativeRoleFrom: accessibleContext ];
+ if ( accessibleContext -> getAccessibleRole() == AccessibleRole::LABEL ) {
+ if ( accessibleContext -> getAccessibleChildCount() > 0 ) {
+ [ nativeRole release ];
+ nativeRole = NSAccessibilityOutlineRole;
+ } else if ( accessibleContext -> getAccessibleParent().is() ) {
+ Reference < XAccessibleContext > rxParentContext = accessibleContext -> getAccessibleParent() -> getAccessibleContext();
+ if ( rxParentContext.is() ) {
+ NSString * roleParent = (NSString *) [ AquaA11yRoleHelper simpleMapNativeRoleFrom: rxParentContext.get() ];
+ if ( [ roleParent isEqualToString: NSAccessibilityOutlineRole ] ) {
+ [ nativeRole release ];
+ nativeRole = NSAccessibilityRowRole;
+ }
+ [ roleParent release ];
+ }
+ }
+ } else if ( accessibleContext -> getAccessibleRole() == AccessibleRole::COMBO_BOX ) {
+ Reference < XAccessible > rxAccessible = accessibleContext -> getAccessibleChild(0);
+ if ( rxAccessible.is() ) {
+ Reference < XAccessibleContext > rxAccessibleContext = rxAccessible -> getAccessibleContext();
+ if ( rxAccessibleContext.is() && rxAccessibleContext -> getAccessibleRole() == AccessibleRole::TEXT ) {
+ if ( ! rxAccessibleContext -> getAccessibleStateSet() -> contains ( AccessibleStateType::EDITABLE ) ) {
+ [ nativeRole release ];
+ nativeRole = NSAccessibilityPopUpButtonRole;
+ }
+ }
+ }
+ }
+ return nativeRole;
+}
+
++(id)getNativeSubroleFrom: (sal_Int16) nRole {
+ id nativeSubrole = nil;
+ switch( nRole ) {
+#define MAP(a,b) \
+ case a: nativeSubrole = b; break
+
+ MAP( AccessibleRole::UNKNOWN, NSAccessibilityUnknownSubrole );
+ MAP( AccessibleRole::ALERT, NSAccessibilitySystemDialogSubrole );
+ MAP( AccessibleRole::COLUMN_HEADER, @"" );
+ MAP( AccessibleRole::CANVAS, @"" );
+ MAP( AccessibleRole::CHECK_BOX, @"" );
+ MAP( AccessibleRole::CHECK_MENU_ITEM, @"" );
+ MAP( AccessibleRole::COLOR_CHOOSER, @"" );
+ MAP( AccessibleRole::COMBO_BOX, @"" );
+ MAP( AccessibleRole::DATE_EDITOR, @"" );
+ MAP( AccessibleRole::DESKTOP_ICON, @"" );
+ MAP( AccessibleRole::DESKTOP_PANE, @"" );
+ MAP( AccessibleRole::DIRECTORY_PANE, @"" );
+ MAP( AccessibleRole::DIALOG, NSAccessibilityDialogSubrole );
+ MAP( AccessibleRole::DOCUMENT, @"" );
+ MAP( AccessibleRole::EMBEDDED_OBJECT, @"" );
+ MAP( AccessibleRole::END_NOTE, @"" );
+ MAP( AccessibleRole::FILE_CHOOSER, @"" );
+ MAP( AccessibleRole::FILLER, @"" );
+ MAP( AccessibleRole::FONT_CHOOSER, @"" );
+ MAP( AccessibleRole::FOOTER, @"" );
+ MAP( AccessibleRole::FOOTNOTE, @"" );
+ MAP( AccessibleRole::FRAME, @"" );
+ MAP( AccessibleRole::GLASS_PANE, @"" );
+ MAP( AccessibleRole::GRAPHIC, @"" );
+ MAP( AccessibleRole::GROUP_BOX, @"" );
+ MAP( AccessibleRole::HEADER, @"" );
+ MAP( AccessibleRole::HEADING, @"" );
+ MAP( AccessibleRole::HYPER_LINK, NSAccessibilityTextLinkSubrole );
+ MAP( AccessibleRole::ICON, @"" );
+ MAP( AccessibleRole::INTERNAL_FRAME, @"" );
+ MAP( AccessibleRole::LABEL, @"" );
+ MAP( AccessibleRole::LAYERED_PANE, @"" );
+ MAP( AccessibleRole::LIST, @"" );
+ MAP( AccessibleRole::LIST_ITEM, NSAccessibilityOutlineRowSubrole );
+ MAP( AccessibleRole::MENU, @"" );
+ MAP( AccessibleRole::MENU_BAR, @"" );
+ MAP( AccessibleRole::MENU_ITEM, @"" );
+ MAP( AccessibleRole::OPTION_PANE, @"" );
+ MAP( AccessibleRole::PAGE_TAB, @"" );
+ MAP( AccessibleRole::PAGE_TAB_LIST, @"" );
+ MAP( AccessibleRole::PANEL, @"" );
+ MAP( AccessibleRole::PARAGRAPH, @"" );
+ MAP( AccessibleRole::PASSWORD_TEXT, NSAccessibilitySecureTextFieldSubrole );
+ MAP( AccessibleRole::POPUP_MENU, @"" );
+ MAP( AccessibleRole::PUSH_BUTTON, @"" );
+ MAP( AccessibleRole::PROGRESS_BAR, @"" );
+ MAP( AccessibleRole::RADIO_BUTTON, @"" );
+ MAP( AccessibleRole::RADIO_MENU_ITEM, @"" );
+ MAP( AccessibleRole::ROW_HEADER, @"" );
+ MAP( AccessibleRole::ROOT_PANE, @"" );
+ MAP( AccessibleRole::SCROLL_BAR, @"" );
+ MAP( AccessibleRole::SCROLL_PANE, @"" );
+ MAP( AccessibleRole::SHAPE, @"" );
+ MAP( AccessibleRole::SEPARATOR, @"" );
+ MAP( AccessibleRole::SLIDER, @"" );
+ MAP( AccessibleRole::SPIN_BOX, @"" );
+ MAP( AccessibleRole::SPLIT_PANE, @"" );
+ MAP( AccessibleRole::STATUS_BAR, @"" );
+ MAP( AccessibleRole::TABLE, @"" );
+ MAP( AccessibleRole::TABLE_CELL, @"" );
+ MAP( AccessibleRole::TEXT, @"" );
+ MAP( AccessibleRole::TEXT_FRAME, @"" );
+ MAP( AccessibleRole::TOGGLE_BUTTON, @"" );
+ MAP( AccessibleRole::TOOL_BAR, @"" );
+ MAP( AccessibleRole::TOOL_TIP, @"" );
+ MAP( AccessibleRole::TREE, @"" );
+ MAP( AccessibleRole::VIEW_PORT, @"" );
+ MAP( AccessibleRole::WINDOW, NSAccessibilityStandardWindowSubrole );
+
+ MAP( AccessibleRole::BUTTON_DROPDOWN, @"" );
+ MAP( AccessibleRole::BUTTON_MENU, @"" );
+ MAP( AccessibleRole::CAPTION, @"" );
+ MAP( AccessibleRole::CHART, @"" );
+ MAP( AccessibleRole::FORM, @"" );
+ MAP( AccessibleRole::IMAGE_MAP, @"" );
+ MAP( AccessibleRole::NOTE, @"" );
+ MAP( AccessibleRole::PAGE, @"" );
+ MAP( AccessibleRole::RULER, @"" );
+ MAP( AccessibleRole::SECTION, @"" );
+ MAP( AccessibleRole::TREE_ITEM, @"" );
+ MAP( AccessibleRole::TREE_TABLE, @"" );
+
+#undef MAP
+ default:
+ break;
+ }
+ return nativeSubrole;
+}
+
++(id)getRoleDescriptionFrom: (NSString *) role with: (NSString *) subRole {
+ id roleDescription;
+ if ( [ subRole length ] == 0 )
+ roleDescription = NSAccessibilityRoleDescription( role, nil );
+ else
+ roleDescription = NSAccessibilityRoleDescription( role, subRole );
+ return roleDescription;
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11yselectionwrapper.h b/vcl/osx/a11yselectionwrapper.h
new file mode 100644
index 000000000000..9b254d7df1f6
--- /dev/null
+++ b/vcl/osx/a11yselectionwrapper.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YSELECTIONWRAPPER_H
+#define INCLUDED_VCL_OSX_A11YSELECTIONWRAPPER_H
+
+#include "osx/osxvcltypes.h"
+#include "osx/a11ywrapper.h"
+
+@interface AquaA11ySelectionWrapper : NSObject
+{
+}
++(id)selectedChildrenAttributeForElement:(AquaA11yWrapper *)wrapper;
++(void)addAttributeNamesTo:(NSMutableArray *)attributeNames;
++(BOOL)isAttributeSettable:(NSString *)attribute forElement:(AquaA11yWrapper *)wrapper;
++(void)setSelectedChildrenAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value;
+@end
+
+#endif // INCLUDED_VCL_OSX_A11YSELECTIONWRAPPER_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11yselectionwrapper.mm b/vcl/osx/a11yselectionwrapper.mm
new file mode 100644
index 000000000000..e341aedacfe0
--- /dev/null
+++ b/vcl/osx/a11yselectionwrapper.mm
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/salinst.h"
+#include "osx/a11yfactory.h"
+
+#include "a11yselectionwrapper.h"
+
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::uno;
+
+@implementation AquaA11ySelectionWrapper : NSObject
+
++(id)selectedChildrenAttributeForElement:(AquaA11yWrapper *)wrapper
+{
+ Reference< XAccessibleSelection > xAccessibleSelection = [ wrapper accessibleSelection ];
+ if( xAccessibleSelection.is() )
+ {
+ NSMutableArray * children = [ [ NSMutableArray alloc ] init ];
+ try {
+ sal_Int32 n = xAccessibleSelection -> getSelectedAccessibleChildCount();
+ for ( sal_Int32 i=0 ; i < n ; ++i ) {
+ [ children addObject: [ AquaA11yFactory wrapperForAccessible: xAccessibleSelection -> getSelectedAccessibleChild( i ) ] ];
+ }
+
+ return children;
+
+ } catch ( Exception& e)
+ {
+ }
+ }
+
+ return nil;
+}
+
+
++(void)addAttributeNamesTo:(NSMutableArray *)attributeNames
+{
+ [ attributeNames addObject: NSAccessibilitySelectedChildrenAttribute ];
+}
+
++(BOOL)isAttributeSettable:(NSString *)attribute forElement:(AquaA11yWrapper *)wrapper
+{
+ (void)wrapper;
+ if ( [ attribute isEqualToString: NSAccessibilitySelectedChildrenAttribute ] )
+ {
+ return YES;
+ }
+ else
+ {
+ return NO;
+ }
+}
+
++(void)setSelectedChildrenAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value
+{
+ Reference< XAccessibleSelection > xAccessibleSelection = [ wrapper accessibleSelection ];
+ try {
+ xAccessibleSelection -> clearAccessibleSelection();
+
+ unsigned c = [ value count ];
+ for ( unsigned i = 0 ; i < c ; ++i ) {
+ xAccessibleSelection -> selectAccessibleChild( [ [ value objectAtIndex: i ] accessibleContext ] -> getAccessibleIndexInParent() );
+ }
+ } catch ( Exception& e) {
+ }
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ytablewrapper.h b/vcl/osx/a11ytablewrapper.h
new file mode 100644
index 000000000000..94b29bd44c78
--- /dev/null
+++ b/vcl/osx/a11ytablewrapper.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YTABLEWRAPPER_H
+#define INCLUDED_VCL_OSX_A11YTABLEWRAPPER_H
+
+#include "osx/a11ywrapper.h"
+
+#define MAXIMUM_ACCESSIBLE_TABLE_CELLS 1000
+
+@interface AquaA11yTableWrapper : AquaA11yWrapper
+{
+}
++(id)childrenAttributeForElement:(AquaA11yTableWrapper *)wrapper;
++(void)addAttributeNamesTo: (NSMutableArray *)attributeNames object: (AquaA11yWrapper*)pObject;
+
+-(id)rowsAttribute;
+-(id)columnsAttribute;
+@end
+#endif // INCLUDED_VCL_OSX_A11YTABLEWRAPPER_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ytablewrapper.mm b/vcl/osx/a11ytablewrapper.mm
new file mode 100644
index 000000000000..f772efcce4b1
--- /dev/null
+++ b/vcl/osx/a11ytablewrapper.mm
@@ -0,0 +1,204 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/a11yfactory.h"
+
+#include "a11ytablewrapper.h"
+
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::uno;
+
+@implementation AquaA11yTableWrapper : AquaA11yWrapper
+
++(id)childrenAttributeForElement:(AquaA11yTableWrapper *)wrapper
+{
+ XAccessibleTable * accessibleTable = [ wrapper accessibleTable ];
+ NSArray* pResult = nil;
+ if( accessibleTable )
+ {
+ NSMutableArray * cells = [ [ NSMutableArray alloc ] init ];
+ try
+ {
+ sal_Int32 nRows = accessibleTable->getAccessibleRowCount();
+ sal_Int32 nCols = accessibleTable->getAccessibleColumnCount();
+
+ if( nRows * nCols < MAXIMUM_ACCESSIBLE_TABLE_CELLS )
+ {
+ // make all children visible to the hierarchy
+ for ( sal_Int32 rowCount = 0; rowCount < nRows; rowCount++ )
+ {
+ for ( sal_Int32 columnCount = 0; columnCount < nCols; columnCount++ )
+ {
+ Reference < XAccessible > rAccessibleCell = accessibleTable -> getAccessibleCellAt ( rowCount, columnCount );
+ if ( rAccessibleCell.is() )
+ {
+ id cell_wrapper = [ AquaA11yFactory wrapperForAccessibleContext: rAccessibleCell -> getAccessibleContext() ];
+ [ cells addObject: cell_wrapper ];
+ [ cell_wrapper release ];
+ }
+ }
+ }
+ }
+ else
+ {
+ XAccessibleComponent * accessibleComponent = [ wrapper accessibleComponent ];
+ // find out which cells are actually visible by determining the top-left-cell and the bottom-right-cell
+ Size tableSize = accessibleComponent -> getSize();
+ Point point;
+ point.X = 0;
+ point.Y = 0;
+ Reference < XAccessible > rAccessibleTopLeft = accessibleComponent -> getAccessibleAtPoint ( point );
+ point.X = tableSize.Width - 1;
+ point.Y = tableSize.Height - 1;
+ Reference < XAccessible > rAccessibleBottomRight = accessibleComponent -> getAccessibleAtPoint ( point );
+ if ( rAccessibleTopLeft.is() && rAccessibleBottomRight.is() )
+ {
+ sal_Int32 idxTopLeft = rAccessibleTopLeft -> getAccessibleContext() -> getAccessibleIndexInParent();
+ sal_Int32 idxBottomRight = rAccessibleBottomRight -> getAccessibleContext() -> getAccessibleIndexInParent();
+ sal_Int32 rowTopLeft = accessibleTable -> getAccessibleRow ( idxTopLeft );
+ sal_Int32 columnTopLeft = accessibleTable -> getAccessibleColumn ( idxTopLeft );
+ sal_Int32 rowBottomRight = accessibleTable -> getAccessibleRow ( idxBottomRight );
+ sal_Int32 columnBottomRight = accessibleTable -> getAccessibleColumn ( idxBottomRight );
+ // create an array containing the visible cells
+ for ( sal_Int32 rowCount = rowTopLeft; rowCount <= rowBottomRight; rowCount++ )
+ {
+ for ( sal_Int32 columnCount = columnTopLeft; columnCount <= columnBottomRight; columnCount++ )
+ {
+ Reference < XAccessible > rAccessibleCell = accessibleTable -> getAccessibleCellAt ( rowCount, columnCount );
+ if ( rAccessibleCell.is() )
+ {
+ id cell_wrapper = [ AquaA11yFactory wrapperForAccessibleContext: rAccessibleCell -> getAccessibleContext() ];
+ [ cells addObject: cell_wrapper ];
+ [ cell_wrapper release ];
+ }
+ }
+ }
+ }
+ }
+ pResult = NSAccessibilityUnignoredChildren( cells );
+ }
+ catch (const Exception &e)
+ {
+ }
+ [cells autorelease];
+ }
+
+ return pResult;
+}
+
++(void)addAttributeNamesTo: (NSMutableArray *)attributeNames object: (AquaA11yWrapper*)pObject
+{
+ XAccessibleTable * accessibleTable = [ pObject accessibleTable ];
+ if( accessibleTable )
+ {
+ sal_Int32 nRows = accessibleTable->getAccessibleRowCount();
+ sal_Int32 nCols = accessibleTable->getAccessibleColumnCount();
+
+
+ if( nRows*nCols < MAXIMUM_ACCESSIBLE_TABLE_CELLS )
+ {
+ [ attributeNames addObject: NSAccessibilityRowsAttribute ];
+ [ attributeNames addObject: NSAccessibilityColumnsAttribute ];
+ }
+ }
+}
+
+-(id)rowsAttribute
+{
+ NSArray* pResult = nil;
+
+ XAccessibleTable * accessibleTable = [ self accessibleTable ];
+ if( accessibleTable )
+ {
+ sal_Int32 nRows = accessibleTable->getAccessibleRowCount();
+ sal_Int32 nCols = accessibleTable->getAccessibleColumnCount();
+ if( nRows * nCols < MAXIMUM_ACCESSIBLE_TABLE_CELLS )
+ {
+ NSMutableArray * cells = [ [ NSMutableArray alloc ] init ];
+ try
+ {
+ // find out number of rows
+ sal_Int32 nRows = accessibleTable->getAccessibleRowCount();
+ for( sal_Int32 n = 0; n < nRows; n++ )
+ {
+ Reference < XAccessible > rAccessibleCell = accessibleTable -> getAccessibleCellAt ( n, 0 );
+ if ( rAccessibleCell.is() )
+ {
+ id cell_wrapper = [ AquaA11yFactory wrapperForAccessibleContext: rAccessibleCell -> getAccessibleContext() ];
+ [ cells addObject: cell_wrapper ];
+ [ cell_wrapper release ];
+ }
+ }
+ pResult = NSAccessibilityUnignoredChildren( cells );
+ }
+ catch (const Exception &e)
+ {
+ pResult = nil;
+ }
+ [ cells autorelease ];
+ }
+ }
+
+ return pResult;
+}
+
+-(id)columnsAttribute
+{
+ NSArray* pResult = nil;
+
+ XAccessibleTable * accessibleTable = [ self accessibleTable ];
+
+ if( accessibleTable )
+ {
+ sal_Int32 nRows = accessibleTable->getAccessibleRowCount();
+ sal_Int32 nCols = accessibleTable->getAccessibleColumnCount();
+ if( nRows * nCols < MAXIMUM_ACCESSIBLE_TABLE_CELLS )
+ {
+ NSMutableArray * cells = [ [ NSMutableArray alloc ] init ];
+ try
+ {
+ // find out number of columns
+ for( sal_Int32 n = 0; n < nCols; n++ )
+ {
+ Reference < XAccessible > rAccessibleCell = accessibleTable -> getAccessibleCellAt ( 0, n );
+ if ( rAccessibleCell.is() )
+ {
+ id cell_wrapper = [ AquaA11yFactory wrapperForAccessibleContext: rAccessibleCell -> getAccessibleContext() ];
+ [ cells addObject: cell_wrapper ];
+ [ cell_wrapper release ];
+ }
+ }
+ pResult = NSAccessibilityUnignoredChildren( cells );
+ }
+ catch (const Exception &e)
+ {
+ pResult = nil;
+ }
+ [ cells autorelease ];
+ }
+ }
+
+ return pResult;
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ytextattributeswrapper.h b/vcl/osx/a11ytextattributeswrapper.h
new file mode 100644
index 000000000000..03e3b3a82eef
--- /dev/null
+++ b/vcl/osx/a11ytextattributeswrapper.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YTEXTATTRIBUTESWRAPPER_H
+#define INCLUDED_VCL_OSX_A11YTEXTATTRIBUTESWRAPPER_H
+
+#include "osx/a11ywrapper.h"
+
+@interface AquaA11yTextAttributesWrapper : NSObject
+{
+}
++(NSMutableAttributedString *)createAttributedStringForElement:(AquaA11yWrapper *)wrapper inOrigRange:(id)origRange;
+@end
+#endif // INCLUDED_VCL_OSX_A11YTEXTATTRIBUTESWRAPPER_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ytextattributeswrapper.mm b/vcl/osx/a11ytextattributeswrapper.mm
new file mode 100644
index 000000000000..339103740be0
--- /dev/null
+++ b/vcl/osx/a11ytextattributeswrapper.mm
@@ -0,0 +1,355 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/salinst.h"
+#include "quartz/utils.h"
+#include "quartz/salgdi.h"
+
+#include "a11ytextattributeswrapper.h"
+
+#include <com/sun/star/accessibility/AccessibleTextType.hpp>
+#include <com/sun/star/awt/FontUnderline.hpp>
+#include <com/sun/star/awt/FontWeight.hpp>
+#include <com/sun/star/awt/FontStrikeout.hpp>
+#include <com/sun/star/text/TextMarkupType.hpp>
+#include <com/sun/star/style/ParagraphAdjust.hpp>
+
+namespace css_awt = ::com::sun::star::awt;
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::rtl;
+
+// cannot use NSFontDescriptor as it has no notion of explicit NSUn{bold,italic}FontMask
+@interface AquaA11yFontDescriptor : NSObject
+{
+ NSString *_name;
+ NSFontTraitMask _traits;
+ CGFloat _size;
+}
+-(void)setName:(NSString*)name;
+-(void)setBold:(NSFontTraitMask)bold;
+-(void)setItalic:(NSFontTraitMask)italic;
+-(void)setSize:(CGFloat)size;
+-(NSFont*)font;
+@end
+
+@implementation AquaA11yFontDescriptor
+- (id)init
+{
+ if((self = [super init]))
+ {
+ _name = nil;
+ _traits = 0;
+ _size = 0.0;
+ }
+ return self;
+}
+
+- (id)initWithDescriptor:(AquaA11yFontDescriptor*)descriptor {
+ if((self = [super init]))
+ {
+ _name = [descriptor->_name retain];
+ _traits = descriptor->_traits;
+ _size = descriptor->_size;
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [_name release];
+ [super dealloc];
+}
+
+-(void)setName:(NSString*)name {
+ if (_name != name) {
+ [name retain];
+ [_name release];
+ _name = name;
+ }
+}
+
+-(void)setBold:(NSFontTraitMask)bold {
+ _traits &= ~(NSBoldFontMask | NSUnboldFontMask);
+ _traits |= bold & (NSBoldFontMask | NSUnboldFontMask);
+};
+
+-(void)setItalic:(NSFontTraitMask)italic {
+ _traits &= ~(NSItalicFontMask | NSUnitalicFontMask);
+ _traits |= italic & (NSItalicFontMask | NSUnitalicFontMask);
+};
+
+-(void)setSize:(CGFloat)size { _size = size; }
+
+-(NSFont*)font {
+ return [[NSFontManager sharedFontManager] fontWithFamily:_name traits:_traits weight:0 size:_size];
+}
+@end
+
+@implementation AquaA11yTextAttributesWrapper : NSObject
+
++(int)convertUnderlineStyle:(PropertyValue)property {
+ int underlineStyle = NSNoUnderlineStyle;
+ sal_Int16 value = 0;
+ property.Value >>= value;
+ if ( value != ::css_awt::FontUnderline::NONE
+ && value != ::css_awt::FontUnderline::DONTKNOW) {
+ underlineStyle = NSSingleUnderlineStyle;
+ }
+ return underlineStyle;
+}
+
++(int)convertBoldStyle:(PropertyValue)property {
+ int boldStyle = NSUnboldFontMask;
+ float value = 0;
+ property.Value >>= value;
+ if ( value == ::css_awt::FontWeight::SEMIBOLD
+ || value == ::css_awt::FontWeight::BOLD
+ || value == ::css_awt::FontWeight::ULTRABOLD
+ || value == ::css_awt::FontWeight::BLACK ) {
+ boldStyle = NSBoldFontMask;
+ }
+ return boldStyle;
+}
+
++(int)convertItalicStyle:(PropertyValue)property {
+ int italicStyle = NSUnitalicFontMask;
+ sal_Int16 value = property.Value.get< ::css_awt::FontSlant>();
+ if ( value == ::css_awt::FontSlant_ITALIC ) {
+ italicStyle = NSItalicFontMask;
+ }
+ return italicStyle;
+}
+
++(BOOL)isStrikethrough:(PropertyValue)property {
+ BOOL strikethrough = NO;
+ sal_Int16 value = 0;
+ property.Value >>= value;
+ if ( value != ::css_awt::FontStrikeout::NONE
+ && value != ::css_awt::FontStrikeout::DONTKNOW ) {
+ strikethrough = YES;
+ }
+ return strikethrough;
+}
+
++(BOOL)convertBoolean:(PropertyValue)property {
+ BOOL myBoolean = NO;
+ bool value = sal_False;
+ property.Value >>= value;
+ if ( value ) {
+ myBoolean = YES;
+ }
+ return myBoolean;
+}
+
++(NSNumber *)convertShort:(PropertyValue)property {
+ sal_Int16 value = 0;
+ property.Value >>= value;
+ return [ NSNumber numberWithShort: value ];
+}
+
++(void)addColor:(SalColor)nSalColor forAttribute:(NSString *)attribute andRange:(NSRange)range toString:(NSMutableAttributedString *)string {
+ if( nSalColor == COL_TRANSPARENT )
+ return;
+ const RGBAColor aRGBAColor( nSalColor);
+ CGColorRef aColorRef = CGColorCreate ( CGColorSpaceCreateWithName ( kCGColorSpaceGenericRGB ), aRGBAColor.AsArray() );
+ [ string addAttribute: attribute value: (id) aColorRef range: range ];
+ CGColorRelease( aColorRef );
+}
+
++(void)addFont:(NSFont *)font toString:(NSMutableAttributedString *)string forRange:(NSRange)range {
+ if ( font != nil ) {
+ NSDictionary * fontDictionary = [ NSDictionary dictionaryWithObjectsAndKeys:
+ [ font fontName ], NSAccessibilityFontNameKey,
+ [ font familyName ], NSAccessibilityFontFamilyKey,
+ [ font displayName ], NSAccessibilityVisibleNameKey,
+ [ NSNumber numberWithFloat: [ font pointSize ] ], NSAccessibilityFontSizeKey,
+ nil
+ ];
+ [ string addAttribute: NSAccessibilityFontTextAttribute
+ value: fontDictionary
+ range: range
+ ];
+ }
+}
+
++(void)applyAttributesFrom:(Sequence < PropertyValue >)attributes toString:(NSMutableAttributedString *)string forRange:(NSRange)range fontDescriptor:(AquaA11yFontDescriptor*)fontDescriptor {
+ NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
+ // constants
+ static const OUString attrUnderline("CharUnderline");
+ static const OUString attrBold("CharWeight");
+ static const OUString attrFontname("CharFontName");
+ static const OUString attrItalic("CharPosture");
+ static const OUString attrHeight("CharHeight");
+ static const OUString attrStrikethrough("CharStrikeout");
+ static const OUString attrShadow("CharShadowed");
+ static const OUString attrUnderlineColor("CharUnderlineColor");
+ static const OUString attrUnderlineHasColor("CharUnderlineHasColor");
+ static const OUString attrForegroundColor("CharColor");
+ static const OUString attrBackgroundColor("CharBackColor");
+ static const OUString attrSuperscript("CharEscapement");
+ static const OUString attrTextAlignment("ParaAdjust");
+ // vars
+ sal_Int32 underlineColor = 0;
+ BOOL underlineHasColor = NO;
+ // add attributes to string
+ for ( int attrIndex = 0; attrIndex < attributes.getLength(); attrIndex++ ) {
+ PropertyValue property = attributes [ attrIndex ];
+ // TODO: NSAccessibilityMisspelledTextAttribute, NSAccessibilityAttachmentTextAttribute, NSAccessibilityLinkTextAttribute
+ // NSAccessibilityStrikethroughColorTextAttribute is unsupported by UNP-API
+ if ( property.Value.hasValue() ) {
+ if ( property.Name.equals ( attrUnderline ) ) {
+ int style = [ AquaA11yTextAttributesWrapper convertUnderlineStyle: property ];
+ if ( style != NSNoUnderlineStyle ) {
+ [ string addAttribute: NSAccessibilityUnderlineTextAttribute value: [ NSNumber numberWithInt: style ] range: range ];
+ }
+ } else if ( property.Name.equals ( attrFontname ) ) {
+ OUString fontname;
+ property.Value >>= fontname;
+ [fontDescriptor setName:CreateNSString(fontname)];
+ } else if ( property.Name.equals ( attrBold ) ) {
+ [fontDescriptor setBold:[AquaA11yTextAttributesWrapper convertBoldStyle:property]];
+ } else if ( property.Name.equals ( attrItalic ) ) {
+ [fontDescriptor setItalic:[AquaA11yTextAttributesWrapper convertItalicStyle:property]];
+ } else if ( property.Name.equals ( attrHeight ) ) {
+ float size;
+ property.Value >>= size;
+ [fontDescriptor setSize:size];
+ } else if ( property.Name.equals ( attrStrikethrough ) ) {
+ if ( [ AquaA11yTextAttributesWrapper isStrikethrough: property ] ) {
+ [ string addAttribute: NSAccessibilityStrikethroughTextAttribute value: [ NSNumber numberWithBool: YES ] range: range ];
+ }
+ } else if ( property.Name.equals ( attrShadow ) ) {
+ if ( [ AquaA11yTextAttributesWrapper convertBoolean: property ] ) {
+ [ string addAttribute: NSAccessibilityShadowTextAttribute value: [ NSNumber numberWithBool: YES ] range: range ];
+ }
+ } else if ( property.Name.equals ( attrUnderlineColor ) ) {
+ property.Value >>= underlineColor;
+ } else if ( property.Name.equals ( attrUnderlineHasColor ) ) {
+ underlineHasColor = [ AquaA11yTextAttributesWrapper convertBoolean: property ];
+ } else if ( property.Name.equals ( attrForegroundColor ) ) {
+ [ AquaA11yTextAttributesWrapper addColor: property.Value.get<sal_Int32>() forAttribute: NSAccessibilityForegroundColorTextAttribute andRange: range toString: string ];
+ } else if ( property.Name.equals ( attrBackgroundColor ) ) {
+ [ AquaA11yTextAttributesWrapper addColor: property.Value.get<sal_Int32>() forAttribute: NSAccessibilityBackgroundColorTextAttribute andRange: range toString: string ];
+ } else if ( property.Name.equals ( attrSuperscript ) ) {
+ // values < zero mean subscript
+ // values > zero mean superscript
+ // this is true for both NSAccessibility-API and UNO-API
+ NSNumber * number = [ AquaA11yTextAttributesWrapper convertShort: property ];
+ if ( [ number shortValue ] != 0 ) {
+ [ string addAttribute: NSAccessibilitySuperscriptTextAttribute value: number range: range ];
+ }
+ } else if ( property.Name.equals ( attrTextAlignment ) ) {
+ sal_Int32 alignment;
+ property.Value >>= alignment;
+ NSNumber *textAlignment = nil;
+ switch(alignment) {
+ case ::com::sun::star::style::ParagraphAdjust_RIGHT : textAlignment = [NSNumber numberWithInteger:NSRightTextAlignment] ; break;
+ case ::com::sun::star::style::ParagraphAdjust_CENTER: textAlignment = [NSNumber numberWithInteger:NSCenterTextAlignment] ; break;
+ case ::com::sun::star::style::ParagraphAdjust_BLOCK : textAlignment = [NSNumber numberWithInteger:NSJustifiedTextAlignment]; break;
+ case ::com::sun::star::style::ParagraphAdjust_LEFT :
+ default : textAlignment = [NSNumber numberWithInteger:NSLeftTextAlignment] ; break;
+ }
+ NSDictionary *paragraphStyle = [NSDictionary dictionaryWithObjectsAndKeys:textAlignment, @"AXTextAlignment", textAlignment, @"AXVisualTextAlignment", nil];
+ [string addAttribute:@"AXParagraphStyle" value:paragraphStyle range:range];
+ }
+ }
+ }
+ // add underline information
+ if ( underlineHasColor ) {
+ [ AquaA11yTextAttributesWrapper addColor: underlineColor forAttribute: NSAccessibilityUnderlineColorTextAttribute andRange: range toString: string ];
+ }
+ // add font information
+ NSFont * font = [fontDescriptor font];
+ [AquaA11yTextAttributesWrapper addFont:font toString:string forRange:range];
+ [ pool release ];
+}
+
++(void)addMarkup:(XAccessibleTextMarkup*)markup withType:(long)type toString:(NSMutableAttributedString*)string inRange:(NSRange)range {
+ const long markupCount = markup->getTextMarkupCount(type);
+ for (long markupIndex = 0; markupIndex < markupCount; ++markupIndex) {
+ TextSegment markupSegment = markup->getTextMarkup(markupIndex, type);
+ NSRange markupRange = NSMakeRange(markupSegment.SegmentStart, markupSegment.SegmentEnd - markupSegment.SegmentStart);
+ markupRange = NSIntersectionRange(range, markupRange);
+ if (markupRange.length > 0) {
+ markupRange.location -= range.location;
+ switch(type) {
+ case ::com::sun::star::text::TextMarkupType::SPELLCHECK: {
+ [string addAttribute:NSAccessibilityMisspelledTextAttribute value:[NSNumber numberWithBool:YES] range:markupRange];
+ [string addAttribute:@"AXMarkedMisspelled" value:[NSNumber numberWithBool:YES] range:markupRange];
+ break;
+ }
+ }
+ }
+ }
+}
+
++(void)addMarkup:(XAccessibleTextMarkup*)markup toString:(NSMutableAttributedString*)string inRange:(NSRange)range {
+ [AquaA11yTextAttributesWrapper addMarkup:markup withType:(::com::sun::star::text::TextMarkupType::SPELLCHECK) toString:string inRange:range];
+}
+
++(NSMutableAttributedString *)createAttributedStringForElement:(AquaA11yWrapper *)wrapper inOrigRange:(id)origRange {
+ static const Sequence < OUString > emptySequence;
+ // vars
+ NSMutableAttributedString * string = nil;
+ int loc = [ origRange rangeValue ].location;
+ int len = [ origRange rangeValue ].length;
+ int endIndex = loc + len;
+ int currentIndex = loc;
+ try {
+ NSString * myString = CreateNSString ( [ wrapper accessibleText ] -> getText() ); // TODO: dirty fix for i87817
+ string = [ [ NSMutableAttributedString alloc ] initWithString: CreateNSString ( [ wrapper accessibleText ] -> getTextRange ( loc, loc + len ) ) ];
+ if ( [ wrapper accessibleTextAttributes ] != nil && [myString characterAtIndex:0] != 57361) { // TODO: dirty fix for i87817
+ [ string beginEditing ];
+ // add default attributes for whole string
+ Sequence < PropertyValue > defaultAttributes = [ wrapper accessibleTextAttributes ] -> getDefaultAttributes ( emptySequence );
+ AquaA11yFontDescriptor *defaultFontDescriptor = [[AquaA11yFontDescriptor alloc] init];
+ [ AquaA11yTextAttributesWrapper applyAttributesFrom: defaultAttributes toString: string forRange: NSMakeRange ( 0, len ) fontDescriptor: defaultFontDescriptor ];
+ // add attributes for attribute run(s)
+ while ( currentIndex < endIndex ) {
+ TextSegment textSegment = [ wrapper accessibleText ] -> getTextAtIndex ( currentIndex, AccessibleTextType::ATTRIBUTE_RUN );
+ int endOfRange = endIndex > textSegment.SegmentEnd ? textSegment.SegmentEnd : endIndex;
+ NSRange rangeForAttributeRun = NSMakeRange ( currentIndex - loc , endOfRange - currentIndex );
+ // add run attributes
+ Sequence < PropertyValue > attributes = [ wrapper accessibleTextAttributes ] -> getRunAttributes ( currentIndex, emptySequence );
+ AquaA11yFontDescriptor *fontDescriptor = [[AquaA11yFontDescriptor alloc] initWithDescriptor:defaultFontDescriptor];
+ [ AquaA11yTextAttributesWrapper applyAttributesFrom: attributes toString: string forRange: rangeForAttributeRun fontDescriptor: fontDescriptor ];
+ [fontDescriptor release];
+ currentIndex = textSegment.SegmentEnd;
+ }
+ [defaultFontDescriptor release];
+ if ([wrapper accessibleTextMarkup])
+ [AquaA11yTextAttributesWrapper addMarkup:[wrapper accessibleTextMarkup] toString:string inRange:[origRange rangeValue]];
+ [ string endEditing ];
+ }
+ } catch ( IllegalArgumentException & e ) {
+ // empty
+ } catch ( IndexOutOfBoundsException & e ) {
+ // empty
+ } catch ( RuntimeException& ) {
+ // at least don't crash
+ }
+ return string;
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ytextwrapper.h b/vcl/osx/a11ytextwrapper.h
new file mode 100644
index 000000000000..c1830a2072c6
--- /dev/null
+++ b/vcl/osx/a11ytextwrapper.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YTEXTWRAPPER_H
+#define INCLUDED_VCL_OSX_A11YTEXTWRAPPER_H
+
+#include "osx/osxvcltypes.h"
+#include "osx/a11ywrapper.h"
+
+@interface AquaA11yTextWrapper : NSObject
+{
+}
++(id)valueAttributeForElement:(AquaA11yWrapper *)wrapper;
++(id)numberOfCharactersAttributeForElement:(AquaA11yWrapper *)wrapper;
++(id)selectedTextAttributeForElement:(AquaA11yWrapper *)wrapper;
++(id)selectedTextRangeAttributeForElement:(AquaA11yWrapper *)wrapper;
++(id)visibleCharacterRangeAttributeForElement:(AquaA11yWrapper *)wrapper;
++(id)sharedTextUIElementsAttributeForElement:(AquaA11yWrapper *)wrapper;
++(id)sharedCharacterRangeAttributeForElement:(AquaA11yWrapper *)wrapper;
++(id)stringForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range;
++(id)attributedStringForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range;
++(id)rangeForIndexAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)index;
++(id)rangeForPositionAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)point;
++(id)boundsForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range;
++(id)styleRangeForIndexAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)index;
++(id)rTFForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range;
++(id)lineForIndexAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)index;
++(id)rangeForLineAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)line;
++(void)addAttributeNamesTo:(NSMutableArray *)attributeNames;
++(void)addParameterizedAttributeNamesTo:(NSMutableArray *)attributeNames;
++(NSArray *)specialAttributeNames;
++(NSArray *)specialParameterizedAttributeNames;
++(BOOL)isAttributeSettable:(NSString *)attribute forElement:(AquaA11yWrapper *)wrapper;
++(void)setVisibleCharacterRangeAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value;
++(void)setSelectedTextRangeAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value;
++(void)setSelectedTextAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value;
++(void)setValueAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value;
+@end
+
+#endif // INCLUDED_VCL_OSX_A11YTEXTWRAPPER_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ytextwrapper.mm b/vcl/osx/a11ytextwrapper.mm
new file mode 100644
index 000000000000..29d525d8b7bf
--- /dev/null
+++ b/vcl/osx/a11ytextwrapper.mm
@@ -0,0 +1,293 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/salinst.h"
+#include "quartz/utils.h"
+#include "a11ytextwrapper.h"
+#include "a11ytextattributeswrapper.h"
+#include "a11yutil.h"
+
+#include <com/sun/star/accessibility/AccessibleTextType.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::rtl;
+
+// Wrapper for XAccessibleText, XAccessibleEditableText and XAccessibleMultiLineText
+
+@implementation AquaA11yTextWrapper : NSObject
+
++(id)valueAttributeForElement:(AquaA11yWrapper *)wrapper {
+ return CreateNSString ( [ wrapper accessibleText ] -> getText() );
+}
+
++(void)setValueAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value
+{
+ // TODO
+ (void)wrapper;
+ (void)value;
+}
+
++(id)numberOfCharactersAttributeForElement:(AquaA11yWrapper *)wrapper {
+ return [ NSNumber numberWithLong: [ wrapper accessibleText ] -> getCharacterCount() ];
+}
+
++(id)selectedTextAttributeForElement:(AquaA11yWrapper *)wrapper {
+ return CreateNSString ( [ wrapper accessibleText ] -> getSelectedText() );
+}
+
++(void)setSelectedTextAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value {
+ if ( [ wrapper accessibleEditableText ] != nil ) {
+ NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
+ OUString newText = GetOUString ( (NSString *) value );
+ NSRange selectedTextRange = [ [ AquaA11yTextWrapper selectedTextRangeAttributeForElement: wrapper ] rangeValue ];
+ try {
+ [ wrapper accessibleEditableText ] -> replaceText ( selectedTextRange.location, selectedTextRange.location + selectedTextRange.length, newText );
+ } catch ( const Exception & e ) {
+ // empty
+ }
+ [ pool release ];
+ }
+}
+
++(id)selectedTextRangeAttributeForElement:(AquaA11yWrapper *)wrapper {
+ sal_Int32 start = [ wrapper accessibleText ] -> getSelectionStart();
+ sal_Int32 end = [ wrapper accessibleText ] -> getSelectionEnd();
+ if ( start != end ) {
+ return [ NSValue valueWithRange: NSMakeRange ( start, end - start ) ]; // true selection
+ } else {
+ long caretPos = [ wrapper accessibleText ] -> getCaretPosition();
+ if ( caretPos < 0 || caretPos > [ wrapper accessibleText ] -> getCharacterCount() ) {
+ return nil;
+ }
+ return [ NSValue valueWithRange: NSMakeRange ( caretPos, 0 ) ]; // insertion point
+ }
+}
+
++(void)setSelectedTextRangeAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value {
+ NSRange range = [ value rangeValue ];
+ try {
+ [ wrapper accessibleText ] -> setSelection ( range.location, range.location + range.length );
+ } catch ( const Exception & e ) {
+ // empty
+ }
+}
+
++(id)visibleCharacterRangeAttributeForElement:(AquaA11yWrapper *)wrapper {
+ // the OOo a11y API returns only the visible portion...
+ return [ NSValue valueWithRange: NSMakeRange ( 0, [ wrapper accessibleText ] -> getCharacterCount() ) ];
+}
+
++(void)setVisibleCharacterRangeAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value
+{
+ // do nothing
+ (void)wrapper;
+ (void)value;
+}
+
++(id)sharedTextUIElementsAttributeForElement:(AquaA11yWrapper *)wrapper
+{
+ (void)wrapper;
+ return [NSArray arrayWithObject:wrapper];
+}
+
++(id)sharedCharacterRangeAttributeForElement:(AquaA11yWrapper *)wrapper
+{
+ (void)wrapper;
+ return [ NSValue valueWithRange: NSMakeRange ( 0, [wrapper accessibleText]->getCharacterCount() ) ];
+}
+
++(void)addAttributeNamesTo:(NSMutableArray *)attributeNames {
+ [ attributeNames addObjectsFromArray: [ AquaA11yTextWrapper specialAttributeNames ] ];
+}
+
++(NSArray *)specialAttributeNames {
+ return [ NSArray arrayWithObjects:
+ NSAccessibilityValueAttribute,
+ NSAccessibilityNumberOfCharactersAttribute,
+ NSAccessibilitySelectedTextAttribute,
+ NSAccessibilitySelectedTextRangeAttribute,
+ NSAccessibilityVisibleCharacterRangeAttribute,
+ NSAccessibilitySharedTextUIElementsAttribute,
+ NSAccessibilitySharedCharacterRangeAttribute,
+ nil ];
+}
+
++(void)addParameterizedAttributeNamesTo:(NSMutableArray *)attributeNames {
+ [ attributeNames addObjectsFromArray: [ AquaA11yTextWrapper specialParameterizedAttributeNames ] ];
+}
+
++(NSArray *)specialParameterizedAttributeNames {
+ return [ NSArray arrayWithObjects:
+ NSAccessibilityStringForRangeParameterizedAttribute,
+ NSAccessibilityAttributedStringForRangeParameterizedAttribute,
+ NSAccessibilityRangeForIndexParameterizedAttribute,
+ NSAccessibilityRangeForPositionParameterizedAttribute,
+ NSAccessibilityBoundsForRangeParameterizedAttribute,
+ NSAccessibilityStyleRangeForIndexParameterizedAttribute,
+ NSAccessibilityRTFForRangeParameterizedAttribute,
+ NSAccessibilityLineForIndexParameterizedAttribute,
+ NSAccessibilityRangeForLineParameterizedAttribute,
+ nil ];
+}
+
++(id)lineForIndexAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)index {
+ NSNumber * lineNumber = nil;
+ try {
+ sal_Int32 line = [ wrapper accessibleMultiLineText ] -> getLineNumberAtIndex ( (sal_Int32) [ index intValue ] );
+ lineNumber = [ NSNumber numberWithInt: line ];
+ } catch ( IndexOutOfBoundsException & e ) {
+ // empty
+ }
+ return lineNumber;
+}
+
++(id)rangeForLineAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)line {
+ NSValue * range = nil;
+ try {
+ TextSegment textSegment = [ wrapper accessibleMultiLineText ] -> getTextAtLineNumber ( [ line intValue ] );
+ range = [ NSValue valueWithRange: NSMakeRange ( textSegment.SegmentStart, textSegment.SegmentEnd - textSegment.SegmentStart ) ];
+ } catch ( IndexOutOfBoundsException & e ) {
+ // empty
+ }
+ return range;
+}
+
++(id)stringForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range {
+ int loc = [ range rangeValue ].location;
+ int len = [ range rangeValue ].length;
+ NSMutableString * textRange = [ [ NSMutableString alloc ] init ];
+ try {
+ [ textRange appendString: CreateNSString ( [ wrapper accessibleText ] -> getTextRange ( loc, loc + len ) ) ];
+ } catch ( IndexOutOfBoundsException & e ) {
+ // empty
+ }
+ return textRange;
+}
+
++(id)attributedStringForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range {
+ return [ AquaA11yTextAttributesWrapper createAttributedStringForElement: wrapper inOrigRange: range ];
+}
+
++(id)rangeForIndexAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)index {
+ NSValue * range = nil;
+ try {
+ TextSegment textSegment = [ wrapper accessibleText ] -> getTextBeforeIndex ( [ index intValue ], AccessibleTextType::GLYPH );
+ range = [ NSValue valueWithRange: NSMakeRange ( textSegment.SegmentStart, textSegment.SegmentEnd - textSegment.SegmentStart ) ];
+ } catch ( IndexOutOfBoundsException & e ) {
+ // empty
+ } catch ( IllegalArgumentException & e ) {
+ // empty
+ }
+ return range;
+}
+
++(id)rangeForPositionAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)point {
+ NSValue * value = nil;
+ sal_Int32 index = [ wrapper accessibleText ] -> getIndexAtPoint ( [ AquaA11yUtil nsPointToVclPoint: point ] );
+ if ( index > -1 ) {
+ value = [ AquaA11yTextWrapper rangeForIndexAttributeForElement: wrapper forParameter: [ NSNumber numberWithLong: index ] ];
+ }
+ return value;
+}
+
++(id)boundsForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range {
+ NSValue * rect = nil;
+ try {
+ // TODO: this is ugly!!!
+ // the UNP-API can only return the bounds for a single character, not for a range
+ int loc = [ range rangeValue ].location;
+ int len = [ range rangeValue ].length;
+ int minx = 0x7fffffff, miny = 0x7fffffff, maxx = 0, maxy = 0;
+ for ( int i = 0; i < len; i++ ) {
+ Rectangle vclRect = [ wrapper accessibleText ] -> getCharacterBounds ( loc + i );
+ if ( vclRect.X < minx ) {
+ minx = vclRect.X;
+ }
+ if ( vclRect.Y < miny ) {
+ miny = vclRect.Y;
+ }
+ if ( vclRect.Width + vclRect.X > maxx ) {
+ maxx = vclRect.Width + vclRect.X;
+ }
+ if ( vclRect.Height + vclRect.Y > maxy ) {
+ maxy = vclRect.Height + vclRect.Y;
+ }
+ }
+ if ( [ wrapper accessibleComponent ] != nil ) {
+ // get location on screen (must be added since get CharacterBounds returns values relative to parent)
+ Point screenPos = [ wrapper accessibleComponent ] -> getLocationOnScreen();
+ Point pos ( minx + screenPos.X, miny + screenPos.Y );
+ Point size ( maxx - minx, maxy - miny );
+ NSValue * nsPos = [ AquaA11yUtil vclPointToNSPoint: pos ];
+ rect = [ NSValue valueWithRect: NSMakeRect ( [ nsPos pointValue ].x, [ nsPos pointValue ].y - size.Y, size.X, size.Y ) ];
+ //printf("Range: %s --- Rect: %s\n", [ NSStringFromRange ( [ range rangeValue ] ) UTF8String ], [ NSStringFromRect ( [ rect rectValue ] ) UTF8String ]);
+ }
+ } catch ( IndexOutOfBoundsException & e ) {
+ // empty
+ }
+ return rect;
+}
+
++(id)styleRangeForIndexAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)index {
+ NSValue * range = nil;
+ try {
+ TextSegment textSegment = [ wrapper accessibleText ] -> getTextAtIndex ( [ index intValue ], AccessibleTextType::ATTRIBUTE_RUN );
+ range = [ NSValue valueWithRange: NSMakeRange ( textSegment.SegmentStart, textSegment.SegmentEnd - textSegment.SegmentStart ) ];
+ } catch ( IndexOutOfBoundsException & e ) {
+ // empty
+ } catch ( IllegalArgumentException & e ) {
+ // empty
+ }
+ return range;
+}
+
++(id)rTFForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range {
+ NSData * rtfData = nil;
+ NSAttributedString * attrString = (NSAttributedString *) [ AquaA11yTextWrapper attributedStringForRangeAttributeForElement: wrapper forParameter: range ];
+ if ( attrString != nil ) {
+ @try {
+ rtfData = [ attrString RTFFromRange: [ range rangeValue ] documentAttributes: nil ];
+ } @catch ( NSException * e) {
+ // emtpy
+ }
+ }
+ return rtfData;
+}
+
++(BOOL)isAttributeSettable:(NSString *)attribute forElement:(AquaA11yWrapper *)wrapper {
+ BOOL isSettable = NO;
+ if ( [ attribute isEqualToString: NSAccessibilityValueAttribute ]
+ || [ attribute isEqualToString: NSAccessibilitySelectedTextAttribute ]
+ || [ attribute isEqualToString: NSAccessibilitySelectedTextRangeAttribute ]
+ || [ attribute isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute ] ) {
+ if ( ! [ [ wrapper accessibilityAttributeValue: NSAccessibilityRoleAttribute ] isEqualToString: NSAccessibilityStaticTextRole ] ) {
+ isSettable = YES;
+ }
+ }
+ return isSettable;
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11yutil.h b/vcl/osx/a11yutil.h
new file mode 100644
index 000000000000..5cc5dc4582f9
--- /dev/null
+++ b/vcl/osx/a11yutil.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YUTIL_H
+#define INCLUDED_VCL_OSX_A11YUTIL_H
+
+#include <com/sun/star/awt/Point.hpp>
+
+@interface AquaA11yUtil : NSObject {
+}
++(NSValue *)vclPointToNSPoint:(::com::sun::star::awt::Point)vclPoint;
++(::com::sun::star::awt::Point)nsPointToVclPoint:(NSValue *)nsPoint;
+@end
+
+#endif // INCLUDED_VCL_OSX_A11YUTIL_H
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11yutil.mm b/vcl/osx/a11yutil.mm
new file mode 100644
index 000000000000..68438643ace7
--- /dev/null
+++ b/vcl/osx/a11yutil.mm
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/osxvcltypes.h"
+
+#include "a11yutil.h"
+
+using namespace ::com::sun::star::awt;
+
+@implementation AquaA11yUtil : NSObject
+
+// TODO: should be merged with AquaSalFrame::VCLToCocoa... to a general helper method
++(NSValue *)vclPointToNSPoint:(Point)vclPoint {
+ // VCL coordinates are in upper-left-notation, Cocoa likes it the Cartesian way (lower-left)
+ NSRect screenRect = [ [ NSScreen mainScreen ] frame ];
+ NSPoint nsPoint = NSMakePoint ( (float) vclPoint.X, (float) ( screenRect.size.height - vclPoint.Y ) );
+ return [ NSValue valueWithPoint: nsPoint ];
+}
+
+// TODO: should be merged with AquaSalFrame::VCLToCocoa... to a general helper method
++(Point)nsPointToVclPoint:(NSValue *)nsPoint {
+ // VCL coordinates are in upper-left-notation, Cocoa likes it the Cartesian way (lower-left)
+ NSRect screenRect = [ [ NSScreen mainScreen ] frame ];
+ return Point ( static_cast<long>([ nsPoint pointValue ].x), static_cast<long>(screenRect.size.height - [ nsPoint pointValue ].y) );
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11yvaluewrapper.h b/vcl/osx/a11yvaluewrapper.h
new file mode 100644
index 000000000000..3ff33d491424
--- /dev/null
+++ b/vcl/osx/a11yvaluewrapper.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YVALUEWRAPPER_H
+#define INCLUDED_VCL_OSX_A11YVALUEWRAPPER_H
+
+#include "osx/salinst.h"
+#include "osx/osxvcltypes.h"
+#include "osx/a11ywrapper.h"
+
+@interface AquaA11yValueWrapper : NSObject
+{
+}
++(id)valueAttributeForElement:(AquaA11yWrapper *)wrapper;
++(id)minValueAttributeForElement:(AquaA11yWrapper *)wrapper;
++(id)maxValueAttributeForElement:(AquaA11yWrapper *)wrapper;
++(void)addAttributeNamesTo:(NSMutableArray *)attributeNames;
++(BOOL)isAttributeSettable:(NSString *)attribute forElement:(AquaA11yWrapper *)wrapper;
++(void)setValueAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value;
+@end
+
+#endif // INCLUDED_VCL_OSX_A11YVALUEWRAPPER_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11yvaluewrapper.mm b/vcl/osx/a11yvaluewrapper.mm
new file mode 100644
index 000000000000..539eb9124f67
--- /dev/null
+++ b/vcl/osx/a11yvaluewrapper.mm
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "a11yvaluewrapper.h"
+#include "a11ywrapperstatictext.h"
+
+using namespace ::com::sun::star::uno;
+
+// Wrapper for XAccessibleValue
+// Remember: A UNO-Value is a single numeric value. Regarding the Mac A11y-API, a value can be anything!
+
+@implementation AquaA11yValueWrapper : NSObject
+
++(id)valueAttributeForElement:(AquaA11yWrapper *)wrapper {
+ // TODO: Detect Type from Any
+ if ( [ wrapper accessibleValue ] != nil ) {
+ long value = 0;
+ [ wrapper accessibleValue ] -> getCurrentValue() >>= value;
+ return [ NSNumber numberWithLong: value ];
+ }
+ return [ NSNumber numberWithLong: 0 ];
+}
+
++(id)minValueAttributeForElement:(AquaA11yWrapper *)wrapper {
+ // TODO: Detect Type from Any
+ if ( [ wrapper accessibleValue ] != nil ) {
+ long value = 0;
+ [ wrapper accessibleValue ] -> getMinimumValue() >>= value;
+ return [ NSNumber numberWithLong: value ];
+ }
+ return [ NSNumber numberWithLong: 0 ];
+}
+
++(id)maxValueAttributeForElement:(AquaA11yWrapper *)wrapper {
+ // TODO: Detect Type from Any
+ if ( [ wrapper accessibleValue ] != nil ) {
+ long value = 0;
+ [ wrapper accessibleValue ] -> getMaximumValue() >>= value;
+ return [ NSNumber numberWithLong: value ];
+ }
+ return [ NSNumber numberWithLong: 0 ];
+}
+
++(void)setValueAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value {
+ // TODO: Detect Type from NSNumber
+ if ( [ value isKindOfClass: [ NSNumber class ] ]
+ && [ wrapper accessibleValue ] != nil ) {
+ NSNumber * number = (NSNumber *) value;
+ Any numberAny ( [ number longValue ] );
+ [ wrapper accessibleValue ] -> setCurrentValue ( numberAny );
+ }
+}
+
++(void)addAttributeNamesTo:(NSMutableArray *)attributeNames {
+ [ attributeNames addObject: NSAccessibilityValueAttribute ];
+}
+
++(BOOL)isAttributeSettable:(NSString *)attribute forElement:(AquaA11yWrapper *)wrapper {
+ BOOL isSettable = NO;
+ if ( [ wrapper accessibleValue ] != nil
+ && [ attribute isEqualToString: NSAccessibilityValueAttribute ]
+ && ! [ wrapper isKindOfClass: [ AquaA11yWrapperStaticText class ] ] ) {
+ isSettable = YES;
+ }
+ return isSettable;
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrapper.mm b/vcl/osx/a11ywrapper.mm
new file mode 100644
index 000000000000..85ee3ff3ab05
--- /dev/null
+++ b/vcl/osx/a11ywrapper.mm
@@ -0,0 +1,1150 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/salinst.h"
+#include "osx/saldata.hxx"
+
+#include "osx/a11ywrapper.h"
+#include "osx/a11ylistener.hxx"
+#include "osx/a11yfactory.h"
+#include "osx/a11yfocustracker.hxx"
+
+#include "quartz/utils.h"
+
+#include "a11yfocuslistener.hxx"
+#include "a11yactionwrapper.h"
+#include "a11ycomponentwrapper.h"
+#include "a11yselectionwrapper.h"
+#include "a11ytablewrapper.h"
+#include "a11ytextwrapper.h"
+#include "a11yvaluewrapper.h"
+#include "a11yrolehelper.h"
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/accessibility/XAccessibleRelationSet.hpp>
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+
+@interface SalFrameWindow : NSWindow
+{
+}
+-(Reference<XAccessibleContext>)accessibleContext;
+@end
+
+static BOOL isPopupMenuOpen = NO;
+
+static std::ostream &operator<<(std::ostream &s, NSObject *obj) {
+ return s << [[obj description] UTF8String];
+}
+
+#ifndef _LP64
+
+// In 64-bit code NSPoint == CGPoint, and CGPoint already has
+// an operator<< in vcl/inc/quartz/util.h
+
+static std::ostream &operator<<(std::ostream &s, NSPoint point) {
+ return s << NSStringFromPoint(point);
+}
+
+#endif
+
+@implementation AquaA11yWrapper : NSView
+
+#pragma mark -
+#pragma mark Init and dealloc
+
+-(id)initWithAccessibleContext: (Reference < XAccessibleContext >) rxAccessibleContext {
+ self = [ super init ];
+ if ( self != nil ) {
+ [ self setDefaults: rxAccessibleContext ];
+ }
+ return self;
+}
+
+-(void) setDefaults: (Reference < XAccessibleContext >) rxAccessibleContext {
+ mpReferenceWrapper = new ReferenceWrapper;
+ mActsAsRadioGroup = NO;
+ mpReferenceWrapper -> rAccessibleContext = rxAccessibleContext;
+ mIsTableCell = NO;
+ // Querying all supported interfaces
+ try {
+ // XAccessibleComponent
+ mpReferenceWrapper -> rAccessibleComponent = Reference < XAccessibleComponent > ( rxAccessibleContext, UNO_QUERY );
+ // XAccessibleExtendedComponent
+ mpReferenceWrapper -> rAccessibleExtendedComponent = Reference < XAccessibleExtendedComponent > ( rxAccessibleContext, UNO_QUERY );
+ // XAccessibleSelection
+ mpReferenceWrapper -> rAccessibleSelection = Reference< XAccessibleSelection > ( rxAccessibleContext, UNO_QUERY );
+ // XAccessibleTable
+ mpReferenceWrapper -> rAccessibleTable = Reference < XAccessibleTable > ( rxAccessibleContext, UNO_QUERY );
+ // XAccessibleText
+ mpReferenceWrapper -> rAccessibleText = Reference < XAccessibleText > ( rxAccessibleContext, UNO_QUERY );
+ // XAccessibleEditableText
+ mpReferenceWrapper -> rAccessibleEditableText = Reference < XAccessibleEditableText > ( rxAccessibleContext, UNO_QUERY );
+ // XAccessibleValue
+ mpReferenceWrapper -> rAccessibleValue = Reference < XAccessibleValue > ( rxAccessibleContext, UNO_QUERY );
+ // XAccessibleAction
+ mpReferenceWrapper -> rAccessibleAction = Reference < XAccessibleAction > ( rxAccessibleContext, UNO_QUERY );
+ // XAccessibleTextAttributes
+ mpReferenceWrapper -> rAccessibleTextAttributes = Reference < XAccessibleTextAttributes > ( rxAccessibleContext, UNO_QUERY );
+ // XAccessibleMultiLineText
+ mpReferenceWrapper -> rAccessibleMultiLineText = Reference < XAccessibleMultiLineText > ( rxAccessibleContext, UNO_QUERY );
+ // XAccessibleTextMarkup
+ mpReferenceWrapper -> rAccessibleTextMarkup = Reference < XAccessibleTextMarkup > ( rxAccessibleContext, UNO_QUERY );
+ // XAccessibleEventBroadcaster
+ #if 0
+ /* #i102033# NSAccessibility does not seemt to know an equivalent for transient children.
+ That means we need to cache this, else e.g. tree list boxes are not accessible (moreover
+ it crashes by notifying dead objects - which would seemt o be another bug)
+
+ FIXME:
+ Unfortunately this can increase memory consumption drastically until the non transient parent
+ is destroyed an finally all the transients are released.
+ */
+ if ( ! rxAccessibleContext -> getAccessibleStateSet() -> contains ( AccessibleStateType::TRANSIENT ) )
+ #endif
+ {
+ Reference< XAccessibleEventBroadcaster > xBroadcaster(rxAccessibleContext, UNO_QUERY);
+ if( xBroadcaster.is() ) {
+ /*
+ * We intentionally do not hold a reference to the event listener in the wrapper object,
+ * but let the listener control the life cycle of the wrapper instead ..
+ */
+ xBroadcaster->addAccessibleEventListener( new AquaA11yEventListener( self, rxAccessibleContext -> getAccessibleRole() ) );
+ }
+ }
+ // TABLE_CELL
+ if ( rxAccessibleContext -> getAccessibleRole() == AccessibleRole::TABLE_CELL ) {
+ mIsTableCell = YES;
+ }
+ } catch ( const Exception ) {
+ }
+}
+
+-(void)dealloc {
+ if ( mpReferenceWrapper != nil ) {
+ delete mpReferenceWrapper;
+ }
+ [ super dealloc ];
+}
+
+#pragma mark -
+#pragma mark Utility Section
+
+// generates selectors for attribute name AXAttributeNameHere
+// (getter without parameter) attributeNameHereAttribute
+// (getter with parameter) attributeNameHereAttributeForParameter:
+// (setter) setAttributeNameHereAttributeForElement:to:
+-(SEL)selectorForAttribute:(NSString *)attribute asGetter:(BOOL)asGetter withGetterParameter:(BOOL)withGetterParameter {
+ SEL selector = nil;
+ NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
+ @try {
+ // step 1: create method name from attribute name
+ NSMutableString * methodName = [ NSMutableString string ];
+ if ( ! asGetter ) {
+ [ methodName appendString: @"set" ];
+ }
+ NSRange aRange = { 2, 1 };
+ NSString * firstChar = [ attribute substringWithRange: aRange ]; // drop leading "AX" and get first char
+ if ( asGetter ) {
+ [ methodName appendString: [ firstChar lowercaseString ] ];
+ } else {
+ [ methodName appendString: firstChar ];
+ }
+ [ methodName appendString: [ attribute substringFromIndex: 3 ] ]; // append rest of attribute name
+ // append rest of method name
+ [ methodName appendString: @"Attribute" ];
+ if ( ! asGetter ) {
+ [ methodName appendString: @"ForElement:to:" ];
+ } else if ( asGetter && withGetterParameter ) {
+ [ methodName appendString: @"ForParameter:" ];
+ }
+ // step 2: create selector
+ selector = NSSelectorFromString ( methodName );
+ } @catch ( id exception ) {
+ selector = nil;
+ }
+ [ pool release ];
+ return selector;
+}
+
+-(Reference < XAccessible >)getFirstRadioButtonInGroup {
+ Reference < XAccessibleRelationSet > rxAccessibleRelationSet = [ self accessibleContext ] -> getAccessibleRelationSet();
+ if( rxAccessibleRelationSet.is() )
+ {
+ AccessibleRelation relationMemberOf = rxAccessibleRelationSet -> getRelationByType ( AccessibleRelationType::MEMBER_OF );
+ if ( relationMemberOf.RelationType == AccessibleRelationType::MEMBER_OF && relationMemberOf.TargetSet.hasElements() )
+ return Reference < XAccessible > ( relationMemberOf.TargetSet[0], UNO_QUERY );
+ }
+ return Reference < XAccessible > ();
+}
+
+-(BOOL)isFirstRadioButtonInGroup {
+ Reference < XAccessible > rFirstMateAccessible = [ self getFirstRadioButtonInGroup ];
+ if ( rFirstMateAccessible.is() && rFirstMateAccessible -> getAccessibleContext().get() == [ self accessibleContext ] ) {
+ return YES;
+ }
+ return NO;
+}
+
+#pragma mark -
+#pragma mark Attribute Value Getters
+// ( called via Reflection by accessibilityAttributeValue )
+
+/*
+ Radiobutton grouping is done differently in NSAccessibility and the UNO-API. In UNO related radio buttons share an entry in their
+ RelationSet. In NSAccessibility the relationship is axpressed through the hierarchy. A AXRadioGroup contains two or more AXRadioButton
+ objects. Since this group is not available in the UNO hierarchy, an extra wrapper is used for it. This wrapper shares almost all
+ attributes with the first radio button of the group, except for the role, subrole, role description, parent and children attributes.
+ So in this five methods there is a special treatment for radio buttons and groups.
+*/
+
+-(id)roleAttribute {
+ if ( mActsAsRadioGroup ) {
+ return NSAccessibilityRadioGroupRole;
+ }
+ else {
+ return [ AquaA11yRoleHelper getNativeRoleFrom: [ self accessibleContext ] ];
+ }
+}
+
+-(id)subroleAttribute {
+ if ( mActsAsRadioGroup ) {
+ return @"";
+ } else {
+ NSString * subRole = [ AquaA11yRoleHelper getNativeSubroleFrom: [ self accessibleContext ] -> getAccessibleRole() ];
+ if ( ! [ subRole isEqualToString: @"" ] ) {
+ return subRole;
+ } else {
+ [ subRole release ];
+ return [ super accessibilityAttributeValue: NSAccessibilitySubroleAttribute ];
+ }
+ }
+}
+
+-(id)titleAttribute {
+ return CreateNSString ( [ self accessibleContext ] -> getAccessibleName() );
+}
+
+-(id)descriptionAttribute {
+ if ( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::COMBO_BOX ) {
+ return [ self titleAttribute ];
+ } else if ( [ self accessibleExtendedComponent ] != nil ) {
+ return [ AquaA11yComponentWrapper descriptionAttributeForElement: self ];
+ } else {
+ return CreateNSString ( [ self accessibleContext ] -> getAccessibleDescription() );
+ }
+}
+
+-(id)enabledAttribute {
+ if ( [ self accessibleContext ] -> getAccessibleStateSet().is() ) {
+ return [ NSNumber numberWithBool: [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::ENABLED ) ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)focusedAttribute {
+ if ( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::COMBO_BOX ) {
+ id isFocused = nil;
+ Reference < XAccessible > rxParent = [ self accessibleContext ] -> getAccessibleParent();
+ if ( rxParent.is() ) {
+ Reference < XAccessibleContext > rxContext = rxParent -> getAccessibleContext();
+ if ( rxContext.is() && rxContext -> getAccessibleStateSet().is() ) {
+ isFocused = [ NSNumber numberWithBool: rxContext -> getAccessibleStateSet() -> contains ( AccessibleStateType::FOCUSED ) ];
+ }
+ }
+ return isFocused;
+ } else if ( [ self accessibleContext ] -> getAccessibleStateSet().is() ) {
+ return [ NSNumber numberWithBool: [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::FOCUSED ) ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)parentAttribute {
+ if ( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::RADIO_BUTTON && ! mActsAsRadioGroup ) {
+ Reference < XAccessible > rxAccessible = [ self getFirstRadioButtonInGroup ];
+ if ( rxAccessible.is() && rxAccessible -> getAccessibleContext().is() ) {
+ Reference < XAccessibleContext > rxAccessibleContext = rxAccessible -> getAccessibleContext();
+ id parent_wrapper = [ AquaA11yFactory wrapperForAccessibleContext: rxAccessibleContext createIfNotExists: YES asRadioGroup: YES ];
+ [ parent_wrapper autorelease ];
+ return NSAccessibilityUnignoredAncestor( parent_wrapper );
+ }
+ return nil;
+ }
+ try {
+ Reference< XAccessible > xParent( [ self accessibleContext ] -> getAccessibleParent() );
+ if ( xParent.is() ) {
+ Reference< XAccessibleContext > xContext( xParent -> getAccessibleContext() );
+ if ( xContext.is() ) {
+ id parent_wrapper = [ AquaA11yFactory wrapperForAccessibleContext: xContext ];
+ [ parent_wrapper autorelease ];
+ return NSAccessibilityUnignoredAncestor( parent_wrapper );
+ }
+ }
+ } catch (const Exception&) {
+ }
+
+ OSL_ASSERT( 0 );
+ return nil;
+}
+
+-(id)childrenAttribute {
+ if ( mActsAsRadioGroup ) {
+ NSMutableArray * children = [ [ NSMutableArray alloc ] init ];
+ Reference < XAccessibleRelationSet > rxAccessibleRelationSet = [ self accessibleContext ] -> getAccessibleRelationSet();
+ AccessibleRelation relationMemberOf = rxAccessibleRelationSet -> getRelationByType ( AccessibleRelationType::MEMBER_OF );
+ if ( relationMemberOf.RelationType == AccessibleRelationType::MEMBER_OF && relationMemberOf.TargetSet.hasElements() ) {
+ for ( int index = 0; index < relationMemberOf.TargetSet.getLength(); index++ ) {
+ Reference < XAccessible > rMateAccessible = Reference < XAccessible > ( relationMemberOf.TargetSet[index], UNO_QUERY );
+ if ( rMateAccessible.is() ) {
+ Reference< XAccessibleContext > rMateAccessibleContext( rMateAccessible -> getAccessibleContext() );
+ if ( rMateAccessibleContext.is() ) {
+ id wrapper = [ AquaA11yFactory wrapperForAccessibleContext: rMateAccessibleContext ];
+ [ children addObject: wrapper ];
+ [ wrapper release ];
+ }
+ }
+ }
+ }
+ return children;
+ } else if ( [ self accessibleTable ] != nil )
+ {
+ AquaA11yTableWrapper* pTable = [self isKindOfClass: [AquaA11yTableWrapper class]] ? (AquaA11yTableWrapper*)self : nil;
+ return [ AquaA11yTableWrapper childrenAttributeForElement: pTable ];
+ } else {
+ try {
+ NSMutableArray * children = [ [ NSMutableArray alloc ] init ];
+ Reference< XAccessibleContext > xContext( [ self accessibleContext ] );
+
+ sal_Int32 cnt = xContext -> getAccessibleChildCount();
+ for ( sal_Int32 i = 0; i < cnt; i++ ) {
+ Reference< XAccessible > xChild( xContext -> getAccessibleChild( i ) );
+ if( xChild.is() ) {
+ Reference< XAccessibleContext > xChildContext( xChild -> getAccessibleContext() );
+ // the menubar is already accessible (including Apple- and Application-Menu) through NSApplication => omit it here
+ if ( xChildContext.is() && AccessibleRole::MENU_BAR != xChildContext -> getAccessibleRole() ) {
+ id wrapper = [ AquaA11yFactory wrapperForAccessibleContext: xChildContext ];
+ [ children addObject: wrapper ];
+ [ wrapper release ];
+ }
+ }
+ }
+
+ // if not already acting as RadioGroup now is the time to replace RadioButtons with RadioGroups and remove RadioButtons
+ if ( ! mActsAsRadioGroup ) {
+ NSEnumerator * enumerator = [ children objectEnumerator ];
+ AquaA11yWrapper * element;
+ while ( ( element = ( (AquaA11yWrapper *) [ enumerator nextObject ] ) ) ) {
+ if ( [ element accessibleContext ] -> getAccessibleRole() == AccessibleRole::RADIO_BUTTON ) {
+ if ( [ element isFirstRadioButtonInGroup ] ) {
+ id wrapper = [ AquaA11yFactory wrapperForAccessibleContext: [ element accessibleContext ] createIfNotExists: YES asRadioGroup: YES ];
+ [ children replaceObjectAtIndex: [ children indexOfObjectIdenticalTo: element ] withObject: wrapper ];
+ }
+ [ children removeObject: element ];
+ }
+ }
+ }
+
+ [ children autorelease ];
+ return NSAccessibilityUnignoredChildren( children );
+ } catch (const Exception &e) {
+ // TODO: Log
+ return nil;
+ }
+ }
+}
+
+-(id)windowAttribute {
+ // go upstairs until reaching the broken connection
+ AquaA11yWrapper * aWrapper = self;
+ int loops = 0;
+ while ( [ aWrapper accessibleContext ] -> getAccessibleParent().is() ) {
+ AquaA11yWrapper *aTentativeParentWrapper = [ AquaA11yFactory wrapperForAccessibleContext: [ aWrapper accessibleContext ] -> getAccessibleParent() -> getAccessibleContext() ];
+ // Quick-and-dirty fix for infinite loop after fixing crash in
+ // fdo#47275
+ if ( aTentativeParentWrapper == aWrapper )
+ break;
+ // Even dirtier fix for infinite loop in fdo#55156
+ if ( loops++ == 100 )
+ break;
+ aWrapper = aTentativeParentWrapper;
+ [ aWrapper autorelease ];
+ }
+ // get associated NSWindow
+ NSView * theView = [ aWrapper viewElementForParent ];
+ return theView;
+}
+
+-(id)topLevelUIElementAttribute {
+ return [ self windowAttribute ];
+}
+
+-(id)sizeAttribute {
+ if ( [ self accessibleComponent ] != nil ) {
+ return [ AquaA11yComponentWrapper sizeAttributeForElement: self ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)positionAttribute {
+ if ( [ self accessibleComponent ] != nil ) {
+ return [ AquaA11yComponentWrapper positionAttributeForElement: self ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)helpAttribute {
+ return CreateNSString ( [ self accessibleContext ] -> getAccessibleDescription() );
+}
+
+-(id)roleDescriptionAttribute {
+ if ( mActsAsRadioGroup ) {
+ return [ AquaA11yRoleHelper getRoleDescriptionFrom: NSAccessibilityRadioGroupRole with: @"" ];
+ } else if( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::RADIO_BUTTON ) {
+ // FIXME: VO should read this because of hierarchy, this is just a workaround
+ // get parent and its children
+ AquaA11yWrapper * parent = [ self parentAttribute ];
+ NSArray * children = [ parent childrenAttribute ];
+ // find index of self
+ int index = 1;
+ NSEnumerator * enumerator = [ children objectEnumerator ];
+ AquaA11yWrapper * child = nil;
+ while ( ( child = [ enumerator nextObject ] ) ) {
+ if ( self == child ) {
+ break;
+ }
+ index++;
+ }
+ // build string
+ NSNumber * nIndex = [ NSNumber numberWithInt: index ];
+ NSNumber * nGroupsize = [ NSNumber numberWithInt: [ children count ] ];
+ NSMutableString * value = [ [ NSMutableString alloc ] init ];
+ [ value appendString: @"radio button " ];
+ [ value appendString: [ nIndex stringValue ] ];
+ [ value appendString: @" of " ];
+ [ value appendString: [ nGroupsize stringValue ] ];
+ // clean up and return string
+ [ nIndex release ];
+ [ nGroupsize release ];
+ [ children release ];
+ return value;
+ } else {
+ return [ AquaA11yRoleHelper getRoleDescriptionFrom:
+ [ AquaA11yRoleHelper getNativeRoleFrom: [ self accessibleContext ] ]
+ with: [ AquaA11yRoleHelper getNativeSubroleFrom: [ self accessibleContext ] -> getAccessibleRole() ] ];
+ }
+}
+
+-(id)valueAttribute {
+ if ( [ [ self roleAttribute ] isEqualToString: NSAccessibilityMenuItemRole ] ) {
+ return nil;
+ } else if ( [ self accessibleText ] != nil ) {
+ return [ AquaA11yTextWrapper valueAttributeForElement: self ];
+ } else if ( [ self accessibleValue ] != nil ) {
+ return [ AquaA11yValueWrapper valueAttributeForElement: self ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)minValueAttribute {
+ if ( [ self accessibleValue ] != nil ) {
+ return [ AquaA11yValueWrapper minValueAttributeForElement: self ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)maxValueAttribute {
+ if ( [ self accessibleValue ] != nil ) {
+ return [ AquaA11yValueWrapper maxValueAttributeForElement: self ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)contentsAttribute {
+ return [ self childrenAttribute ];
+}
+
+-(id)selectedChildrenAttribute {
+ return [ AquaA11ySelectionWrapper selectedChildrenAttributeForElement: self ];
+}
+
+-(id)numberOfCharactersAttribute {
+ if ( [ self accessibleText ] != nil ) {
+ return [ AquaA11yTextWrapper numberOfCharactersAttributeForElement: self ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)selectedTextAttribute {
+ if ( [ self accessibleText ] != nil ) {
+ return [ AquaA11yTextWrapper selectedTextAttributeForElement: self ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)selectedTextRangeAttribute {
+ if ( [ self accessibleText ] != nil ) {
+ return [ AquaA11yTextWrapper selectedTextRangeAttributeForElement: self ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)visibleCharacterRangeAttribute {
+ if ( [ self accessibleText ] != nil ) {
+ return [ AquaA11yTextWrapper visibleCharacterRangeAttributeForElement: self ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)tabsAttribute {
+ return self; // TODO ???
+}
+
+-(id)sharedTextUIElementsAttribute {
+ if ( [ self accessibleText ] != nil ) {
+ return [ AquaA11yTextWrapper sharedTextUIElementsAttributeForElement: self ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)sharedCharacterRangeAttribute {
+ if ( [ self accessibleText ] != nil ) {
+ return [ AquaA11yTextWrapper sharedCharacterRangeAttributeForElement: self ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)expandedAttribute {
+ return [ NSNumber numberWithBool: [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::EXPANDED ) ];
+}
+
+-(id)selectedAttribute {
+ return [ NSNumber numberWithBool: [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::SELECTED ) ];
+}
+
+-(id)stringForRangeAttributeForParameter:(id)range {
+ if ( [ self accessibleText ] != nil ) {
+ return [ AquaA11yTextWrapper stringForRangeAttributeForElement: self forParameter: range ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)attributedStringForRangeAttributeForParameter:(id)range {
+ if ( [ self accessibleText ] != nil ) {
+ return [ AquaA11yTextWrapper attributedStringForRangeAttributeForElement: self forParameter: range ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)rangeForIndexAttributeForParameter:(id)index {
+ if ( [ self accessibleText ] != nil ) {
+ return [ AquaA11yTextWrapper rangeForIndexAttributeForElement: self forParameter: index ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)rangeForPositionAttributeForParameter:(id)point {
+ if ( [ self accessibleText ] != nil ) {
+ return [ AquaA11yTextWrapper rangeForPositionAttributeForElement: self forParameter: point ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)boundsForRangeAttributeForParameter:(id)range {
+ if ( [ self accessibleText ] != nil ) {
+ return [ AquaA11yTextWrapper boundsForRangeAttributeForElement: self forParameter: range ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)styleRangeForIndexAttributeForParameter:(id)index {
+ if ( [ self accessibleText ] != nil ) {
+ return [ AquaA11yTextWrapper styleRangeForIndexAttributeForElement: self forParameter: index ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)rTFForRangeAttributeForParameter:(id)range {
+ if ( [ self accessibleText ] != nil ) {
+ return [ AquaA11yTextWrapper rTFForRangeAttributeForElement: self forParameter: range ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)orientationAttribute {
+ NSString * orientation = nil;
+ Reference < XAccessibleStateSet > stateSet = [ self accessibleContext ] -> getAccessibleStateSet();
+ if ( stateSet -> contains ( AccessibleStateType::HORIZONTAL ) ) {
+ orientation = NSAccessibilityHorizontalOrientationValue;
+ } else if ( stateSet -> contains ( AccessibleStateType::VERTICAL ) ) {
+ orientation = NSAccessibilityVerticalOrientationValue;
+ }
+ return orientation;
+}
+
+-(id)titleUIElementAttribute {
+ if ( [ self accessibleContext ] -> getAccessibleRelationSet().is() ) {
+ NSString * title = [ self titleAttribute ];
+ id titleElement = nil;
+ if ( [ title length ] == 0 ) {
+ AccessibleRelation relationLabeledBy = [ self accessibleContext ] -> getAccessibleRelationSet() -> getRelationByType ( AccessibleRelationType::LABELED_BY );
+ if ( relationLabeledBy.RelationType == AccessibleRelationType::LABELED_BY && relationLabeledBy.TargetSet.hasElements() ) {
+ Reference < XAccessible > rxAccessible ( relationLabeledBy.TargetSet[0], UNO_QUERY );
+ titleElement = [ AquaA11yFactory wrapperForAccessibleContext: rxAccessible -> getAccessibleContext() ];
+ }
+ }
+ if ( title != nil ) {
+ [ title release ];
+ }
+ return titleElement;
+ } else {
+ return nil;
+ }
+}
+
+-(id)servesAsTitleForUIElementsAttribute {
+ if ( [ self accessibleContext ] -> getAccessibleRelationSet().is() ) {
+ id titleForElement = nil;
+ AccessibleRelation relationLabelFor = [ self accessibleContext ] -> getAccessibleRelationSet() -> getRelationByType ( AccessibleRelationType::LABEL_FOR );
+ if ( relationLabelFor.RelationType == AccessibleRelationType::LABEL_FOR && relationLabelFor.TargetSet.hasElements() ) {
+ Reference < XAccessible > rxAccessible ( relationLabelFor.TargetSet[0], UNO_QUERY );
+ titleForElement = [ AquaA11yFactory wrapperForAccessibleContext: rxAccessible -> getAccessibleContext() ];
+ }
+ return titleForElement;
+ } else {
+ return nil;
+ }
+}
+
+-(id)lineForIndexAttributeForParameter:(id)index {
+ if ( [ self accessibleMultiLineText ] != nil ) {
+ return [ AquaA11yTextWrapper lineForIndexAttributeForElement: self forParameter: index ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)rangeForLineAttributeForParameter:(id)line {
+ if ( [ self accessibleMultiLineText ] != nil ) {
+ return [ AquaA11yTextWrapper rangeForLineAttributeForElement: self forParameter: line ];
+ } else {
+ return nil;
+ }
+}
+
+#pragma mark -
+#pragma mark Accessibility Protocol
+
+-(id)accessibilityAttributeValue:(NSString *)attribute {
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilityAttributeValue:" << attribute << "]");
+ // #i90575# guard NSAccessibility protocol against unwanted access
+ if ( isPopupMenuOpen ) {
+ return nil;
+ }
+
+ id value = nil;
+ // if we are no longer in the wrapper repository, we have been disposed
+ AquaA11yWrapper * theWrapper = [ AquaA11yFactory wrapperForAccessibleContext: [ self accessibleContext ] createIfNotExists: NO ];
+ if ( theWrapper != nil || mIsTableCell ) {
+ try {
+ SEL methodSelector = [ self selectorForAttribute: attribute asGetter: YES withGetterParameter: NO ];
+ if ( [ self respondsToSelector: methodSelector ] ) {
+ value = [ self performSelector: methodSelector ];
+ }
+ } catch ( const DisposedException & e ) {
+ mIsTableCell = NO; // just to be sure
+ [ AquaA11yFactory removeFromWrapperRepositoryFor: [ self accessibleContext ] ];
+ return nil;
+ } catch ( const Exception & e ) {
+ // empty
+ }
+ }
+ if ( theWrapper != nil ) {
+ [ theWrapper release ]; // the above called method calls retain on the returned Wrapper
+ }
+ return value;
+}
+
+-(BOOL)accessibilityIsIgnored {
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilityIsIgnored]");
+ // #i90575# guard NSAccessibility protocol against unwanted access
+ if ( isPopupMenuOpen ) {
+ return NO;
+ }
+ BOOL ignored = NO;
+ sal_Int16 nRole = [ self accessibleContext ] -> getAccessibleRole();
+ switch ( nRole ) {
+ case AccessibleRole::PANEL:
+ case AccessibleRole::FRAME:
+ case AccessibleRole::ROOT_PANE:
+ case AccessibleRole::SEPARATOR:
+ case AccessibleRole::FILLER:
+ case AccessibleRole::DIALOG:
+ ignored = YES;
+ break;
+ default:
+ ignored = ! ( [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::VISIBLE ) );
+ break;
+ }
+ return ignored; // TODO: to be completed
+}
+
+-(NSArray *)accessibilityAttributeNames {
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilityAttributeNames]");
+ // #i90575# guard NSAccessibility protocol against unwanted access
+ if ( isPopupMenuOpen ) {
+ return nil;
+ }
+ NSString * nativeSubrole = nil;
+ NSString * title = nil;
+ NSMutableArray * attributeNames = nil;
+ sal_Int32 nAccessibleChildren = 0;
+ try {
+ // Default Attributes
+ attributeNames = [ NSMutableArray arrayWithObjects:
+ NSAccessibilityRoleAttribute,
+ NSAccessibilityDescriptionAttribute,
+ NSAccessibilityParentAttribute,
+ NSAccessibilityWindowAttribute,
+ NSAccessibilityHelpAttribute,
+ NSAccessibilityTopLevelUIElementAttribute,
+ NSAccessibilityRoleDescriptionAttribute,
+ nil ];
+ nativeSubrole = (NSString *) [ AquaA11yRoleHelper getNativeSubroleFrom: [ self accessibleContext ] -> getAccessibleRole() ];
+ title = (NSString *) [ self titleAttribute ];
+ Reference < XAccessibleRelationSet > rxRelationSet = [ self accessibleContext ] -> getAccessibleRelationSet();
+ // Special Attributes depending on attribute values
+ if ( nativeSubrole != nil && ! [ nativeSubrole isEqualToString: @"" ] ) {
+ [ attributeNames addObject: NSAccessibilitySubroleAttribute ];
+ }
+ try
+ {
+ nAccessibleChildren = [ self accessibleContext ] -> getAccessibleChildCount();
+ if ( nAccessibleChildren > 0 ) {
+ [ attributeNames addObject: NSAccessibilityChildrenAttribute ];
+ }
+ }
+ catch( DisposedException& ) {}
+ catch( RuntimeException& ) {}
+
+ if ( title != nil && ! [ title isEqualToString: @"" ] ) {
+ [ attributeNames addObject: NSAccessibilityTitleAttribute ];
+ }
+ if ( [ title length ] == 0 && rxRelationSet.is() && rxRelationSet -> containsRelation ( AccessibleRelationType::LABELED_BY ) ) {
+ [ attributeNames addObject: NSAccessibilityTitleUIElementAttribute ];
+ }
+ if ( rxRelationSet.is() && rxRelationSet -> containsRelation ( AccessibleRelationType::LABEL_FOR ) ) {
+ [ attributeNames addObject: NSAccessibilityServesAsTitleForUIElementsAttribute ];
+ }
+ // Special Attributes depending on interface
+ if( [self accessibleContext ] -> getAccessibleRole() == AccessibleRole::TABLE )
+ [AquaA11yTableWrapper addAttributeNamesTo: attributeNames object: self];
+
+ if ( [ self accessibleText ] != nil ) {
+ [ AquaA11yTextWrapper addAttributeNamesTo: attributeNames ];
+ }
+ if ( [ self accessibleComponent ] != nil ) {
+ [ AquaA11yComponentWrapper addAttributeNamesTo: attributeNames ];
+ }
+ if ( [ self accessibleSelection ] != nil ) {
+ [ AquaA11ySelectionWrapper addAttributeNamesTo: attributeNames ];
+ }
+ if ( [ self accessibleValue ] != nil ) {
+ [ AquaA11yValueWrapper addAttributeNamesTo: attributeNames ];
+ }
+ [ nativeSubrole release ];
+ [ title release ];
+ return attributeNames;
+ } catch ( DisposedException & e ) { // Object is no longer available
+ if ( nativeSubrole != nil ) {
+ [ nativeSubrole release ];
+ }
+ if ( title != nil ) {
+ [ title release ];
+ }
+ if ( attributeNames != nil ) {
+ [ attributeNames release ];
+ }
+ [ AquaA11yFactory removeFromWrapperRepositoryFor: [ self accessibleContext ] ];
+ return [ [ NSArray alloc ] init ];
+ }
+}
+
+-(BOOL)accessibilityIsAttributeSettable:(NSString *)attribute {
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilityAttributeIsSettable:" << attribute << "]");
+ BOOL isSettable = NO;
+ if ( [ self accessibleText ] != nil ) {
+ isSettable = [ AquaA11yTextWrapper isAttributeSettable: attribute forElement: self ];
+ }
+ if ( ! isSettable && [ self accessibleComponent ] != nil ) {
+ isSettable = [ AquaA11yComponentWrapper isAttributeSettable: attribute forElement: self ];
+ }
+ if ( ! isSettable && [ self accessibleSelection ] != nil ) {
+ isSettable = [ AquaA11ySelectionWrapper isAttributeSettable: attribute forElement: self ];
+ }
+ if ( ! isSettable && [ self accessibleValue ] != nil ) {
+ isSettable = [ AquaA11yValueWrapper isAttributeSettable: attribute forElement: self ];
+ }
+ return isSettable; // TODO: to be completed
+}
+
+-(NSArray *)accessibilityParameterizedAttributeNames {
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilityParameterizedAttributeNames]");
+ NSMutableArray * attributeNames = [ [ NSMutableArray alloc ] init ];
+ // Special Attributes depending on interface
+ if ( [ self accessibleText ] != nil ) {
+ [ AquaA11yTextWrapper addParameterizedAttributeNamesTo: attributeNames ];
+ }
+ return attributeNames; // TODO: to be completed
+}
+
+-(id)accessibilityAttributeValue:(NSString *)attribute forParameter:(id)parameter {
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilityAttributeValue:" << attribute << " forParameter:" << ((NSObject*)parameter) << "]");
+ SEL methodSelector = [ self selectorForAttribute: attribute asGetter: YES withGetterParameter: YES ];
+ if ( [ self respondsToSelector: methodSelector ] ) {
+ return [ self performSelector: methodSelector withObject: parameter ];
+ }
+ return nil; // TODO: to be completed
+}
+
+-(BOOL)accessibilitySetOverrideValue:(id)value forAttribute:(NSString *)attribute
+{
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilitySetOverrideValue:" << ((NSObject*)value) << " forAttribute:" << attribute << "]");
+ (void)value;
+ (void)attribute;
+ return NO; // TODO
+}
+
+-(void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute {
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilitySetValue:" << ((NSObject*)value) << " forAttribute:" << attribute << "]");
+ SEL methodSelector = [ self selectorForAttribute: attribute asGetter: NO withGetterParameter: NO ];
+ if ( [ AquaA11yComponentWrapper respondsToSelector: methodSelector ] ) {
+ [ AquaA11yComponentWrapper performSelector: methodSelector withObject: self withObject: value ];
+ }
+ if ( [ AquaA11yTextWrapper respondsToSelector: methodSelector ] ) {
+ [ AquaA11yTextWrapper performSelector: methodSelector withObject: self withObject: value ];
+ }
+ if ( [ AquaA11ySelectionWrapper respondsToSelector: methodSelector ] ) {
+ [ AquaA11ySelectionWrapper performSelector: methodSelector withObject: self withObject: value ];
+ }
+ if ( [ AquaA11yValueWrapper respondsToSelector: methodSelector ] ) {
+ [ AquaA11yValueWrapper performSelector: methodSelector withObject: self withObject: value ];
+ }
+}
+
+-(id)accessibilityFocusedUIElement {
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilityFocusedUIElement]");
+ // #i90575# guard NSAccessibility protocol against unwanted access
+ if ( isPopupMenuOpen ) {
+ return nil;
+ }
+
+ // as this seems to be the first API call on a newly created SalFrameView object,
+ // make sure self gets registered in the repository ..
+ [ self accessibleContext ];
+
+ AquaA11yWrapper * focusedUIElement = AquaA11yFocusListener::get()->getFocusedUIElement();
+// AquaA11yWrapper * ancestor = focusedUIElement;
+
+ // Make sure the focused object is a descendant of self
+// do {
+// if( self == ancestor )
+ return focusedUIElement;
+
+// ancestor = [ ancestor accessibilityAttributeValue: NSAccessibilityParentAttribute ];
+// } while( nil != ancestor );
+
+ return self;
+}
+
+-(NSString *)accessibilityActionDescription:(NSString *)action {
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilityActionDescription:" << action << "]");
+ return NSAccessibilityActionDescription(action);
+}
+
+-(AquaA11yWrapper *)actionResponder {
+ AquaA11yWrapper * wrapper = nil;
+ // get some information
+ NSString * role = (NSString *) [ self accessibilityAttributeValue: NSAccessibilityRoleAttribute ];
+ id enabledAttr = [ self enabledAttribute ];
+ BOOL enabled = [ enabledAttr boolValue ];
+ NSView * parent = (NSView *) [ self accessibilityAttributeValue: NSAccessibilityParentAttribute ];
+ AquaA11yWrapper * parentAsWrapper = nil;
+ if ( [ parent isKindOfClass: [ AquaA11yWrapper class ] ] ) {
+ parentAsWrapper = (AquaA11yWrapper *) parent;
+ }
+ NSString * parentRole = (NSString *) [ parent accessibilityAttributeValue: NSAccessibilityRoleAttribute ];
+ // if we are a textarea inside a combobox, then the combobox is the action responder
+ if ( enabled
+ && [ role isEqualToString: NSAccessibilityTextAreaRole ]
+ && [ parentRole isEqualToString: NSAccessibilityComboBoxRole ]
+ && parentAsWrapper != nil ) {
+ wrapper = parentAsWrapper;
+ } else if ( enabled && [ self accessibleAction ] != nil ) {
+ wrapper = self ;
+ }
+ [ parentRole release ];
+ [ enabledAttr release ];
+ [ role release ];
+ return wrapper;
+}
+
+-(void)accessibilityPerformAction:(NSString *)action {
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilityPerformAction:" << action << "]");
+ AquaA11yWrapper * actionResponder = [ self actionResponder ];
+ if ( actionResponder != nil ) {
+ [ AquaA11yActionWrapper doAction: action ofElement: actionResponder ];
+ }
+}
+
+-(NSArray *)accessibilityActionNames {
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilityActionNames]");
+ NSArray * actionNames = nil;
+ AquaA11yWrapper * actionResponder = [ self actionResponder ];
+ if ( actionResponder != nil ) {
+ actionNames = [ AquaA11yActionWrapper actionNamesForElement: actionResponder ];
+ } else {
+ actionNames = [ [ NSArray alloc ] init ];
+ }
+ return actionNames;
+}
+
+#pragma mark -
+#pragma mark Hit Test
+
+-(BOOL)isViewElement:(NSObject *)viewElement hitByPoint:(NSPoint)point {
+ BOOL hit = NO;
+ NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
+ NSValue * position = [ viewElement accessibilityAttributeValue: NSAccessibilityPositionAttribute ];
+ NSValue * size = [ viewElement accessibilityAttributeValue: NSAccessibilitySizeAttribute ];
+ if ( position != nil && size != nil ) {
+ float minX = [ position pointValue ].x;
+ float minY = [ position pointValue ].y;
+ float maxX = minX + [ size sizeValue ].width;
+ float maxY = minY + [ size sizeValue ].height;
+ if ( minX < point.x && maxX > point.x && minY < point.y && maxY > point.y ) {
+ hit = YES;
+ }
+ }
+ [ pool release ];
+ return hit;
+}
+
+Reference < XAccessibleContext > hitTestRunner ( com::sun::star::awt::Point point,
+ Reference < XAccessibleContext > rxAccessibleContext ) {
+ Reference < XAccessibleContext > hitChild;
+ Reference < XAccessibleContext > emptyReference;
+ try {
+ Reference < XAccessibleComponent > rxAccessibleComponent ( rxAccessibleContext, UNO_QUERY );
+ if ( rxAccessibleComponent.is() ) {
+ com::sun::star::awt::Point location = rxAccessibleComponent -> getLocationOnScreen();
+ com::sun::star::awt::Point hitPoint ( point.X - location.X , point.Y - location.Y);
+ Reference < XAccessible > rxAccessible = rxAccessibleComponent -> getAccessibleAtPoint ( hitPoint );
+ if ( rxAccessible.is() && rxAccessible -> getAccessibleContext().is() &&
+ rxAccessible -> getAccessibleContext() -> getAccessibleChildCount() == 0 ) {
+ hitChild = rxAccessible -> getAccessibleContext();
+ }
+ }
+
+ // iterate the hirerachy looking doing recursive hit testing.
+ // apparently necessary as a special treatment for e.g. comboboxes
+ if ( !hitChild.is() ) {
+ bool bSafeToIterate = true;
+ sal_Int32 nCount = rxAccessibleContext -> getAccessibleChildCount();
+
+ if ( nCount < 0 || nCount > SAL_MAX_UINT16 /* slow enough for anyone */ )
+ bSafeToIterate = false;
+ else { // manages descendants is an horror from the a11y standards guys.
+ Reference< XAccessibleStateSet > xStateSet;
+ xStateSet = rxAccessibleContext -> getAccessibleStateSet();
+ if (xStateSet.is() && xStateSet -> contains(AccessibleStateType::MANAGES_DESCENDANTS ) )
+ bSafeToIterate = false;
+ }
+
+ if( bSafeToIterate ) {
+ for ( int i = 0; i < rxAccessibleContext -> getAccessibleChildCount(); i++ ) {
+ Reference < XAccessible > rxAccessibleChild = rxAccessibleContext -> getAccessibleChild ( i );
+ if ( rxAccessibleChild.is() && rxAccessibleChild -> getAccessibleContext().is() && rxAccessibleChild -> getAccessibleContext() -> getAccessibleRole() != AccessibleRole::LIST ) {
+ Reference < XAccessibleContext > myHitChild = hitTestRunner ( point, rxAccessibleChild -> getAccessibleContext() );
+ if ( myHitChild.is() ) {
+ hitChild = myHitChild;
+ break;
+ }
+ }
+ }
+ }
+ }
+ } catch ( RuntimeException ) {
+ return emptyReference;
+ }
+ return hitChild;
+}
+
+-(id)accessibilityHitTest:(NSPoint)point {
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilityHitTest:" << point << "]");
+ static id wrapper = nil;
+ if ( nil != wrapper ) {
+ [ wrapper release ];
+ wrapper = nil;
+ }
+ Reference < XAccessibleContext > hitChild;
+ NSRect screenRect = [ [ NSScreen mainScreen ] frame ];
+ com::sun::star::awt::Point hitPoint ( static_cast<long>(point.x) , static_cast<long>(screenRect.size.height - point.y) );
+ // check child windows first
+ NSWindow * window = (NSWindow *) [ self accessibilityAttributeValue: NSAccessibilityWindowAttribute ];
+ NSArray * childWindows = [ window childWindows ];
+ if ( [ childWindows count ] > 0 ) {
+ NSWindow * element = nil;
+ NSEnumerator * enumerator = [ childWindows objectEnumerator ];
+ while ( ( element = [ enumerator nextObject ] ) && hitChild == nil ) {
+ if ( [ element isKindOfClass: [ SalFrameWindow class ] ] && [ self isViewElement: element hitByPoint: point ] ) {
+ // we have a child window that is hit
+ Reference < XAccessibleRelationSet > relationSet = [ ( ( SalFrameWindow * ) element ) accessibleContext ] -> getAccessibleRelationSet();
+ if ( relationSet.is() && relationSet -> containsRelation ( AccessibleRelationType::SUB_WINDOW_OF )) {
+ // we have a valid relation to the parent element
+ AccessibleRelation relation = relationSet -> getRelationByType ( AccessibleRelationType::SUB_WINDOW_OF );
+ for ( int i = 0; i < relation.TargetSet.getLength() && !hitChild.is(); i++ ) {
+ Reference < XAccessible > rxAccessible ( relation.TargetSet [ i ], UNO_QUERY );
+ if ( rxAccessible.is() && rxAccessible -> getAccessibleContext().is() ) {
+ // hit test for children of parent
+ hitChild = hitTestRunner ( hitPoint, rxAccessible -> getAccessibleContext() );
+ }
+ }
+ }
+ }
+ }
+ }
+ // nothing hit yet, so check ourself
+ if ( ! hitChild.is() ) {
+ if ( mpReferenceWrapper == nil ) {
+ [ self setDefaults: [ self accessibleContext ] ];
+ }
+ hitChild = hitTestRunner ( hitPoint, mpReferenceWrapper -> rAccessibleContext );
+ }
+ if ( hitChild.is() ) {
+ wrapper = [ AquaA11yFactory wrapperForAccessibleContext: hitChild ];
+ }
+ if ( wrapper != nil ) {
+ [ wrapper retain ]; // TODO: retain only when transient ?
+ }
+ return wrapper;
+}
+
+#pragma mark -
+#pragma mark Access Methods
+
+-(XAccessibleAction *)accessibleAction {
+ return mpReferenceWrapper -> rAccessibleAction.get();
+}
+
+-(XAccessibleContext *)accessibleContext {
+ return mpReferenceWrapper -> rAccessibleContext.get();
+}
+
+-(XAccessibleComponent *)accessibleComponent {
+ return mpReferenceWrapper -> rAccessibleComponent.get();
+}
+
+-(XAccessibleExtendedComponent *)accessibleExtendedComponent {
+ return mpReferenceWrapper -> rAccessibleExtendedComponent.get();
+}
+
+-(XAccessibleSelection *)accessibleSelection {
+ return mpReferenceWrapper -> rAccessibleSelection.get();
+}
+
+-(XAccessibleTable *)accessibleTable {
+ return mpReferenceWrapper -> rAccessibleTable.get();
+}
+
+-(XAccessibleText *)accessibleText {
+ return mpReferenceWrapper -> rAccessibleText.get();
+}
+
+-(XAccessibleEditableText *)accessibleEditableText {
+ return mpReferenceWrapper -> rAccessibleEditableText.get();
+}
+
+-(XAccessibleValue *)accessibleValue {
+ return mpReferenceWrapper -> rAccessibleValue.get();
+}
+
+-(XAccessibleTextAttributes *)accessibleTextAttributes {
+ return mpReferenceWrapper -> rAccessibleTextAttributes.get();
+}
+
+-(XAccessibleMultiLineText *)accessibleMultiLineText {
+ return mpReferenceWrapper -> rAccessibleMultiLineText.get();
+}
+
+-(XAccessibleTextMarkup *)accessibleTextMarkup {
+ return mpReferenceWrapper -> rAccessibleTextMarkup.get();
+}
+
+-(NSView *)viewElementForParent {
+ return self;
+}
+
+// These four are for AXTextAreas only. They are needed, because bold and italic
+// attributes have to be bound to a font on the Mac. Our UNO-API instead handles
+// and reports them independently. When they occur we bundle them to a font with
+// this information here to create a according NSFont.
+-(void)setActsAsRadioGroup:(BOOL)actsAsRadioGroup {
+ mActsAsRadioGroup = actsAsRadioGroup;
+}
+
+-(BOOL)actsAsRadioGroup {
+ return mActsAsRadioGroup;
+}
+
++(void)setPopupMenuOpen:(BOOL)popupMenuOpen {
+ isPopupMenuOpen = popupMenuOpen;
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrapperbutton.h b/vcl/osx/a11ywrapperbutton.h
new file mode 100644
index 000000000000..0bf115f130be
--- /dev/null
+++ b/vcl/osx/a11ywrapperbutton.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YWRAPPERBUTTON_H
+#define INCLUDED_VCL_OSX_A11YWRAPPERBUTTON_H
+
+#include "osx/a11ywrapper.h"
+
+@interface AquaA11yWrapperButton : AquaA11yWrapper
+{
+}
+-(id)valueAttribute;
+-(id)descriptionAttribute;
+-(NSArray *)accessibilityAttributeNames;
+@end
+
+#endif // INCLUDED_VCL_OSX_A11YWRAPPERBUTTON_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrapperbutton.mm b/vcl/osx/a11ywrapperbutton.mm
new file mode 100644
index 000000000000..eab7e07d00e3
--- /dev/null
+++ b/vcl/osx/a11ywrapperbutton.mm
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/salinst.h"
+
+#include "a11ywrapperbutton.h"
+#include "a11ytextwrapper.h"
+
+// Wrapper for AXButton role
+
+@implementation AquaA11yWrapperButton : AquaA11yWrapper
+
+-(id)valueAttribute {
+ return [ NSString string ]; // we propagate AXTitle, that's enough
+}
+
+-(id)descriptionAttribute {
+ return [ NSString string ]; // we propagate AXTitle, that's enough
+}
+
+-(NSArray *)accessibilityAttributeNames {
+ // Default Attributes
+ NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ];
+ // Special Attributes and removing unwanted attributes depending on role
+ if ( [ attributeNames containsObject: NSAccessibilityTitleAttribute ] ) {
+ [ attributeNames removeObject: NSAccessibilityDescriptionAttribute ];
+ } else {
+ [ attributeNames addObject: NSAccessibilityTitleAttribute ];
+ }
+ // Remove text-specific attributes
+ [ attributeNames removeObjectsInArray: [ AquaA11yTextWrapper specialAttributeNames ] ];
+ return attributeNames;
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrappercheckbox.h b/vcl/osx/a11ywrappercheckbox.h
new file mode 100644
index 000000000000..58cd2986e41e
--- /dev/null
+++ b/vcl/osx/a11ywrappercheckbox.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YWRAPPERCHECKBOX_H
+#define INCLUDED_VCL_OSX_A11YWRAPPERCHECKBOX_H
+
+#include "osx/a11ywrapper.h"
+
+@interface AquaA11yWrapperCheckBox : AquaA11yWrapper
+{
+}
+-(id)valueAttribute;
+-(BOOL)accessibilityIsAttributeSettable:(NSString *)attribute;
+-(NSArray *)accessibilityAttributeNames;
+@end
+
+#endif // INCLUDED_VCL_OSX_A11YWRAPPERCHECKBOX_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrappercheckbox.mm b/vcl/osx/a11ywrappercheckbox.mm
new file mode 100644
index 000000000000..e8ea2a00b9d6
--- /dev/null
+++ b/vcl/osx/a11ywrappercheckbox.mm
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/salinst.h"
+
+#include "a11ywrappercheckbox.h"
+#include "a11yvaluewrapper.h"
+#include "a11ytextwrapper.h"
+
+// Wrapper for AXCheckbox role
+
+@implementation AquaA11yWrapperCheckBox : AquaA11yWrapper
+
+-(id)valueAttribute {
+ if ( [ self accessibleValue ] != nil ) {
+ return [ AquaA11yValueWrapper valueAttributeForElement: self ];
+ }
+ return [ NSNumber numberWithInt: 0 ];
+}
+
+-(BOOL)accessibilityIsAttributeSettable:(NSString *)attribute {
+ if ( [ attribute isEqualToString: NSAccessibilityValueAttribute ] ) {
+ return NO;
+ }
+ return [ super accessibilityIsAttributeSettable: attribute ];
+}
+
+-(NSArray *)accessibilityAttributeNames {
+ // Default Attributes
+ NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ];
+ // Remove text-specific attributes
+ [ attributeNames removeObjectsInArray: [ AquaA11yTextWrapper specialAttributeNames ] ];
+ [ attributeNames addObject: NSAccessibilityValueAttribute ];
+ [ attributeNames addObject: NSAccessibilityMinValueAttribute ];
+ [ attributeNames addObject: NSAccessibilityMaxValueAttribute ];
+ return attributeNames;
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrappercombobox.h b/vcl/osx/a11ywrappercombobox.h
new file mode 100644
index 000000000000..13058844b746
--- /dev/null
+++ b/vcl/osx/a11ywrappercombobox.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YWRAPPERCOMBOBOX_H
+#define INCLUDED_VCL_OSX_A11YWRAPPERCOMBOBOX_H
+
+#include "osx/a11ywrapper.h"
+#include <com/sun/star/accessibility/XAccessibleContext.hpp>
+
+@interface AquaA11yWrapperComboBox : AquaA11yWrapper
+{
+ AquaA11yWrapper * textArea;
+}
+-(id)initWithAccessibleContext: (::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleContext >) anAccessibleContext;
+-(id)valueAttribute;
+-(id)numberOfCharactersAttribute;
+-(id)selectedTextAttribute;
+-(id)selectedTextRangeAttribute;
+-(id)visibleCharacterRangeAttribute;
+// Accessibility Protocol
+-(BOOL)accessibilityIsAttributeSettable:(NSString *)attribute;
+-(void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute;
+-(NSArray *)accessibilityAttributeNames;
+@end
+
+#endif // INCLUDED_VCL_OSX_A11YWRAPPERCOMBOBOX_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrappercombobox.mm b/vcl/osx/a11ywrappercombobox.mm
new file mode 100644
index 000000000000..2467718c79d4
--- /dev/null
+++ b/vcl/osx/a11ywrappercombobox.mm
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/salinst.h"
+
+#include "a11ywrappercombobox.h"
+#include "a11yrolehelper.h"
+
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::uno;
+
+// Wrapper for AXCombobox role
+
+@implementation AquaA11yWrapperComboBox : AquaA11yWrapper
+
+#pragma mark -
+#pragma mark Specialized Init Method
+
+-(id)initWithAccessibleContext: (Reference < XAccessibleContext >) rxAccessibleContext {
+ self = [ super initWithAccessibleContext: rxAccessibleContext ];
+ if ( self != nil )
+ {
+ textArea = nil;
+ }
+ return self;
+}
+
+#pragma mark -
+#pragma mark Private Helper Method
+
+-(AquaA11yWrapper *)textArea {
+ // FIXME: May cause problems when stored. Then get dynamically each time (bad performance!)
+ if ( textArea == nil ) {
+ NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
+ NSArray * elementChildren = [ super childrenAttribute ];
+ if ( [ elementChildren count ] > 0 ) {
+ NSEnumerator * enumerator = [ elementChildren objectEnumerator ];
+ id child;
+ while ( ( child = [ enumerator nextObject ] ) ) {
+ AquaA11yWrapper * element = ( AquaA11yWrapper * ) child;
+ if ( [ [ AquaA11yRoleHelper getNativeRoleFrom: [ element accessibleContext ] ] isEqualToString: NSAccessibilityTextAreaRole ] ) {
+ textArea = element;
+ break;
+ }
+ }
+ }
+ [ pool release ];
+ }
+ return textArea;
+}
+
+#pragma mark -
+#pragma mark Wrapped Attributes From Contained Text Area
+
+-(id)valueAttribute {
+ if ( [ self textArea ] != nil ) {
+ return [ [ self textArea ] valueAttribute ];
+ }
+ return @"";
+}
+
+-(id)numberOfCharactersAttribute {
+ if ( [ self textArea ] != nil ) {
+ return [ [ self textArea ] numberOfCharactersAttribute ];
+ }
+ return [ NSNumber numberWithInt: 0 ];
+}
+
+-(id)selectedTextAttribute {
+ if ( [ self textArea ] != nil ) {
+ return [ [ self textArea ] selectedTextAttribute ];
+ }
+ return @"";
+}
+
+-(id)selectedTextRangeAttribute {
+ if ( [ self textArea ] != nil ) {
+ return [ [ self textArea ] selectedTextRangeAttribute ];
+ }
+ return [ NSValue valueWithRange: NSMakeRange ( 0, 0 ) ];
+}
+
+-(id)visibleCharacterRangeAttribute {
+ if ( [ self textArea ] != nil ) {
+ return [ [ self textArea ] visibleCharacterRangeAttribute ];
+ }
+ return [ NSValue valueWithRange: NSMakeRange ( 0, 0 ) ];
+}
+
+#pragma mark -
+#pragma mark Accessibility Protocol
+
+-(BOOL)accessibilityIsAttributeSettable:(NSString *)attribute {
+ if ( [ self textArea ] != nil && (
+ [ attribute isEqualToString: NSAccessibilitySelectedTextAttribute ]
+ || [ attribute isEqualToString: NSAccessibilitySelectedTextRangeAttribute ]
+ || [ attribute isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute ] ) ) {
+ return [ [ self textArea ] accessibilityIsAttributeSettable: attribute ];
+ }
+ return [ super accessibilityIsAttributeSettable: attribute ];
+}
+
+-(void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute {
+ if ( [ self textArea ] != nil && (
+ [ attribute isEqualToString: NSAccessibilitySelectedTextAttribute ]
+ || [ attribute isEqualToString: NSAccessibilitySelectedTextRangeAttribute ]
+ || [ attribute isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute ] ) ) {
+ return [ [ self textArea ] accessibilitySetValue: value forAttribute: attribute ];
+ }
+ return [ super accessibilitySetValue: value forAttribute: attribute ];
+}
+
+-(NSArray *)accessibilityAttributeNames {
+ // Default Attributes
+ NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ];
+ // Special Attributes and removing unwanted attributes depending on role
+ [ attributeNames removeObjectsInArray: [ NSArray arrayWithObjects:
+ NSAccessibilityTitleAttribute,
+ NSAccessibilityChildrenAttribute,
+ nil ]
+ ];
+ [ attributeNames addObjectsFromArray: [ NSArray arrayWithObjects:
+ NSAccessibilityExpandedAttribute,
+ NSAccessibilityValueAttribute,
+ NSAccessibilityNumberOfCharactersAttribute,
+ NSAccessibilitySelectedTextAttribute,
+ NSAccessibilitySelectedTextRangeAttribute,
+ NSAccessibilityVisibleCharacterRangeAttribute,
+ nil ]
+ ];
+ return attributeNames;
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrappergroup.h b/vcl/osx/a11ywrappergroup.h
new file mode 100644
index 000000000000..af2baea63e11
--- /dev/null
+++ b/vcl/osx/a11ywrappergroup.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YWRAPPERGROUP_H
+#define INCLUDED_VCL_OSX_A11YWRAPPERGROUP_H
+
+#include "osx/a11ywrapper.h"
+
+@interface AquaA11yWrapperGroup : AquaA11yWrapper
+{
+}
+-(id)titleUIElementAttribute;
+-(NSArray *)accessibilityAttributeNames;
+@end
+
+#endif // INCLUDED_VCL_OSX_A11YWRAPPERGROUP_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrappergroup.mm b/vcl/osx/a11ywrappergroup.mm
new file mode 100644
index 000000000000..3eb864fc7eb8
--- /dev/null
+++ b/vcl/osx/a11ywrappergroup.mm
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/salinst.h"
+#include "a11ywrappergroup.h"
+
+// Wrapper for AXGroup role
+
+@implementation AquaA11yWrapperGroup : AquaA11yWrapper
+
+-(id)titleUIElementAttribute {
+ return self; // TODO
+}
+
+-(NSArray *)accessibilityAttributeNames {
+ // Default Attributes
+ NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ];
+ // Special Attributes and removing unwanted attributes depending on role
+ [ attributeNames removeObjectsInArray: [ NSArray arrayWithObjects:
+ NSAccessibilityTitleAttribute,
+ NSAccessibilityEnabledAttribute,
+ NSAccessibilitySelectedChildrenAttribute,
+ nil ]
+ ];
+ [ attributeNames addObject: NSAccessibilityContentsAttribute ];
+ [ attributeNames addObject: NSAccessibilityTitleUIElementAttribute ];
+ return attributeNames;
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrapperlist.h b/vcl/osx/a11ywrapperlist.h
new file mode 100644
index 000000000000..65f605a173b1
--- /dev/null
+++ b/vcl/osx/a11ywrapperlist.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YWRAPPERLIST_H
+#define INCLUDED_VCL_OSX_A11YWRAPPERLIST_H
+
+#include "osx/a11ywrapper.h"
+
+@interface AquaA11yWrapperList : AquaA11yWrapper
+{
+}
+-(NSArray *)accessibilityAttributeNames;
+@end
+
+#endif // INCLUDED_VCL_OSX_A11YWRAPPERLIST_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrapperlist.mm b/vcl/osx/a11ywrapperlist.mm
new file mode 100644
index 000000000000..e10bf727858f
--- /dev/null
+++ b/vcl/osx/a11ywrapperlist.mm
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/salinst.h"
+#include "a11ywrapperlist.h"
+
+using namespace ::com::sun::star::accessibility;
+
+// Wrapper for AXList role
+
+@implementation AquaA11yWrapperList : AquaA11yWrapper
+
+-(NSArray *)accessibilityAttributeNames {
+ // Default Attributes
+ NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ];
+ // Special Attributes and removing unwanted attributes depending on role
+ [ attributeNames addObject: NSAccessibilityOrientationAttribute ];
+ return attributeNames;
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrapperradiobutton.h b/vcl/osx/a11ywrapperradiobutton.h
new file mode 100644
index 000000000000..83e3f5fc9e89
--- /dev/null
+++ b/vcl/osx/a11ywrapperradiobutton.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YWRAPPERRADIOBUTTON_H
+#define INCLUDED_VCL_OSX_A11YWRAPPERRADIOBUTTON_H
+
+#include "osx/a11ywrapper.h"
+
+@interface AquaA11yWrapperRadioButton : AquaA11yWrapper
+{
+}
+-(id)valueAttribute;
+-(BOOL)accessibilityIsAttributeSettable:(NSString *)attribute;
+-(NSArray *)accessibilityAttributeNames;
+@end
+
+#endif // INCLUDED_VCL_OSX_A11YWRAPPERRADIOBUTTON_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrapperradiobutton.mm b/vcl/osx/a11ywrapperradiobutton.mm
new file mode 100644
index 000000000000..cfd8dbd23871
--- /dev/null
+++ b/vcl/osx/a11ywrapperradiobutton.mm
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/salinst.h"
+#include "a11ywrapperradiobutton.h"
+#include "a11ytextwrapper.h"
+#include "a11yvaluewrapper.h"
+
+// Wrapper for AXRadioButton role
+
+@implementation AquaA11yWrapperRadioButton : AquaA11yWrapper
+
+-(id)valueAttribute {
+ if ( [ self accessibleValue ] != nil ) {
+ return [ AquaA11yValueWrapper valueAttributeForElement: self ];
+ }
+ return [ NSNumber numberWithInt: 0 ];
+}
+
+-(BOOL)accessibilityIsAttributeSettable:(NSString *)attribute {
+ if ( [ attribute isEqualToString: NSAccessibilityValueAttribute ] ) {
+ return NO;
+ }
+ return [ super accessibilityIsAttributeSettable: attribute ];
+}
+
+-(NSArray *)accessibilityAttributeNames {
+ // Default Attributes
+ NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ];
+ // Special Attributes and removing unwanted attributes depending on role
+ [ attributeNames removeObjectsInArray: [ AquaA11yTextWrapper specialAttributeNames ] ];
+ [ attributeNames addObject: NSAccessibilityMinValueAttribute ];
+ [ attributeNames addObject: NSAccessibilityMaxValueAttribute ];
+ [ attributeNames addObject: NSAccessibilityValueAttribute ];
+ return attributeNames;
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrapperradiogroup.h b/vcl/osx/a11ywrapperradiogroup.h
new file mode 100644
index 000000000000..6ae98cbfd583
--- /dev/null
+++ b/vcl/osx/a11ywrapperradiogroup.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YWRAPPERRADIOGROUP_H
+#define INCLUDED_VCL_OSX_A11YWRAPPERRADIOGROUP_H
+
+#include "osx/a11ywrapper.h"
+
+@interface AquaA11yWrapperRadioGroup : AquaA11yWrapper
+{
+}
+-(NSArray *)accessibilityAttributeNames;
+@end
+
+#endif // INCLUDED_VCL_OSX_A11YWRAPPERRADIOGROUP_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrapperradiogroup.mm b/vcl/osx/a11ywrapperradiogroup.mm
new file mode 100644
index 000000000000..d66192df67bb
--- /dev/null
+++ b/vcl/osx/a11ywrapperradiogroup.mm
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/salinst.h"
+#include "a11ywrapperradiogroup.h"
+#include "a11ytextwrapper.h"
+
+// Wrapper for AXRadioGroup role
+
+@implementation AquaA11yWrapperRadioGroup : AquaA11yWrapper
+
+-(NSArray *)accessibilityAttributeNames {
+ // Default Attributes
+ NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ];
+ // Special Attributes and removing unwanted attributes depending on role
+ [ attributeNames removeObjectsInArray: [ AquaA11yTextWrapper specialAttributeNames ] ];
+ [ attributeNames removeObject: NSAccessibilityTitleAttribute ];
+ return attributeNames;
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrapperrow.h b/vcl/osx/a11ywrapperrow.h
new file mode 100644
index 000000000000..45cd7477d6ec
--- /dev/null
+++ b/vcl/osx/a11ywrapperrow.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YWRAPPERROW_H
+#define INCLUDED_VCL_OSX_A11YWRAPPERROW_H
+
+#include "osx/a11ywrapper.h"
+
+@interface AquaA11yWrapperRow : AquaA11yWrapper
+{
+}
+-(id)disclosingAttribute;
+-(NSArray *)accessibilityAttributeNames;
+@end
+
+#endif // INCLUDED_VCL_OSX_A11YWRAPPERROW_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrapperrow.mm b/vcl/osx/a11ywrapperrow.mm
new file mode 100644
index 000000000000..8bcef8032cd3
--- /dev/null
+++ b/vcl/osx/a11ywrapperrow.mm
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/salinst.h"
+
+#include "a11ywrapperrow.h"
+#include "a11ytextwrapper.h"
+
+// Wrapper for AXRow role
+
+@implementation AquaA11yWrapperRow : AquaA11yWrapper
+
+-(id)disclosingAttribute {
+ return NULL; // TODO
+}
+
+-(NSArray *)accessibilityAttributeNames {
+ // Default Attributes
+ NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ];
+ // Special Attributes and removing unwanted attributes depending on role
+ [ attributeNames removeObjectsInArray: [ AquaA11yTextWrapper specialAttributeNames ] ];
+ [ attributeNames removeObject: NSAccessibilityTitleAttribute ];
+ [ attributeNames removeObject: NSAccessibilityEnabledAttribute ];
+ [ attributeNames removeObject: NSAccessibilityFocusedAttribute ];
+ [ attributeNames addObject: NSAccessibilitySelectedAttribute ];
+ [ attributeNames addObject: NSAccessibilityDisclosingAttribute ];
+ return attributeNames;
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrapperscrollarea.h b/vcl/osx/a11ywrapperscrollarea.h
new file mode 100644
index 000000000000..35a2aa15a028
--- /dev/null
+++ b/vcl/osx/a11ywrapperscrollarea.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YWRAPPERSCROLLAREA_H
+#define INCLUDED_VCL_OSX_A11YWRAPPERSCROLLAREA_H
+
+#include "osx/a11ywrapper.h"
+
+@interface AquaA11yWrapperScrollArea : AquaA11yWrapper
+{
+}
+-(id)verticalScrollBarAttribute;
+-(id)horizontalScrollBarAttribute;
+-(NSArray *)accessibilityAttributeNames;
+@end
+
+#endif // INCLUDED_VCL_OSX_A11YWRAPPERSCROLLAREA_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrapperscrollarea.mm b/vcl/osx/a11ywrapperscrollarea.mm
new file mode 100644
index 000000000000..f70f44962ddb
--- /dev/null
+++ b/vcl/osx/a11ywrapperscrollarea.mm
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/salinst.h"
+
+#include "a11ywrapperscrollarea.h"
+#include "a11ywrapperscrollbar.h"
+#include "a11yrolehelper.h"
+
+// Wrapper for AXScrollArea role
+
+@implementation AquaA11yWrapperScrollArea : AquaA11yWrapper
+
+-(id)scrollBarWithOrientation:(NSString *)orientation {
+ AquaA11yWrapper * theScrollBar = nil;
+ NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
+ NSArray * elementChildren = [ self accessibilityAttributeValue: NSAccessibilityChildrenAttribute ];
+ if ( [ elementChildren count ] > 0 ) {
+ NSEnumerator * enumerator = [ elementChildren objectEnumerator ];
+ id child;
+ while ( ( child = [ enumerator nextObject ] ) ) {
+ AquaA11yWrapper * element = ( AquaA11yWrapper * ) child;
+ if ( [ element isKindOfClass: [ AquaA11yWrapperScrollBar class ] ] ) {
+ AquaA11yWrapperScrollBar * scrollBar = (AquaA11yWrapperScrollBar *) element;
+ if ( [ [ scrollBar orientationAttribute ] isEqualToString: orientation ] ) {
+ theScrollBar = scrollBar;
+ break;
+ }
+ }
+ }
+ }
+ [ pool release ];
+ return theScrollBar;
+}
+
+-(id)verticalScrollBarAttribute {
+ return [ self scrollBarWithOrientation: NSAccessibilityVerticalOrientationValue ];
+}
+
+-(id)horizontalScrollBarAttribute {
+ return [ self scrollBarWithOrientation: NSAccessibilityHorizontalOrientationValue ];
+}
+
+-(NSArray *)accessibilityAttributeNames {
+ // Default Attributes
+ NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ];
+ // Special Attributes and removing unwanted attributes depending on role
+ [ attributeNames removeObject: NSAccessibilityEnabledAttribute ];
+ [ attributeNames addObjectsFromArray: [ NSArray arrayWithObjects:
+ NSAccessibilityContentsAttribute,
+ NSAccessibilityVerticalScrollBarAttribute,
+ NSAccessibilityHorizontalScrollBarAttribute,
+ nil ]
+ ];
+ return attributeNames;
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrapperscrollbar.h b/vcl/osx/a11ywrapperscrollbar.h
new file mode 100644
index 000000000000..a6d87079a31b
--- /dev/null
+++ b/vcl/osx/a11ywrapperscrollbar.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YWRAPPERSCROLLBAR_H
+#define INCLUDED_VCL_OSX_A11YWRAPPERSCROLLBAR_H
+
+#include "osx/a11ywrapper.h"
+
+@interface AquaA11yWrapperScrollBar : AquaA11yWrapper
+{
+}
+-(NSArray *)accessibilityAttributeNames;
+@end
+
+#endif // INCLUDED_VCL_OSX_A11YWRAPPERSCROLLBAR_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrapperscrollbar.mm b/vcl/osx/a11ywrapperscrollbar.mm
new file mode 100644
index 000000000000..3717cafc883b
--- /dev/null
+++ b/vcl/osx/a11ywrapperscrollbar.mm
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/salinst.h"
+
+#include "a11ywrapperscrollbar.h"
+
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+
+using namespace ::com::sun::star::accessibility;
+
+// Wrapper for AXScrollBar role
+
+@implementation AquaA11yWrapperScrollBar : AquaA11yWrapper
+
+-(NSArray *)accessibilityAttributeNames {
+ // Default Attributes
+ NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ];
+ // Special Attributes and removing unwanted attributes depending on role
+ [ attributeNames addObject: NSAccessibilityOrientationAttribute ];
+ return attributeNames;
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrappersplitter.h b/vcl/osx/a11ywrappersplitter.h
new file mode 100644
index 000000000000..18cdb6e0cdca
--- /dev/null
+++ b/vcl/osx/a11ywrappersplitter.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YWRAPPERSPLITTER_H
+#define INCLUDED_VCL_OSX_A11YWRAPPERSPLITTER_H
+
+#include "osx/a11ywrapper.h"
+
+@interface AquaA11yWrapperSplitter : AquaA11yWrapper
+{
+}
+-(NSArray *)accessibilityAttributeNames;
+@end
+
+#endif // INCLUDED_VCL_OSX_A11YWRAPPERSPLITTER_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrappersplitter.mm b/vcl/osx/a11ywrappersplitter.mm
new file mode 100644
index 000000000000..66db6dc92f36
--- /dev/null
+++ b/vcl/osx/a11ywrappersplitter.mm
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/salinst.h"
+#include "a11ywrappersplitter.h"
+
+using namespace ::com::sun::star::accessibility;
+
+// Wrapper for AXSplitter role
+
+@implementation AquaA11yWrapperSplitter : AquaA11yWrapper
+
+-(NSArray *)accessibilityAttributeNames {
+ // Default Attributes
+ NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ];
+ // Special Attributes and removing unwanted attributes depending on role
+ [ attributeNames addObject: NSAccessibilityOrientationAttribute ];
+ return attributeNames;
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrapperstatictext.h b/vcl/osx/a11ywrapperstatictext.h
new file mode 100644
index 000000000000..a210d80b7550
--- /dev/null
+++ b/vcl/osx/a11ywrapperstatictext.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YWRAPPERSTATICTEXT_H
+#define INCLUDED_VCL_OSX_A11YWRAPPERSTATICTEXT_H
+
+#include "osx/a11ywrapper.h"
+
+@interface AquaA11yWrapperStaticText : AquaA11yWrapper
+{
+}
+-(id)titleAttribute;
+-(NSArray *)accessibilityAttributeNames;
+@end
+
+#endif // INCLUDED_VCL_OSX_A11YWRAPPERSTATICTEXT_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrapperstatictext.mm b/vcl/osx/a11ywrapperstatictext.mm
new file mode 100644
index 000000000000..ce0d7c425d92
--- /dev/null
+++ b/vcl/osx/a11ywrapperstatictext.mm
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/salinst.h"
+#include "a11ywrapperstatictext.h"
+
+// Wrapper for AXStaticText role
+
+@implementation AquaA11yWrapperStaticText : AquaA11yWrapper
+
+-(id)titleAttribute {
+ NSString * title = [ super titleAttribute ];
+ if ( [ title isEqualToString: [ super valueAttribute ] ] ) {
+ return [ NSString string ];
+ }
+ return title;
+}
+
+-(NSArray *)accessibilityAttributeNames {
+ // Default Attributes
+ NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ];
+ // Special Attributes and removing unwanted attributes depending on role
+ [ attributeNames removeObject: NSAccessibilityTitleAttribute ];
+ [ attributeNames removeObject: NSAccessibilitySharedTextUIElementsAttribute ];
+ [ attributeNames removeObject: NSAccessibilitySharedCharacterRangeAttribute ];
+ return attributeNames;
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrappertabgroup.h b/vcl/osx/a11ywrappertabgroup.h
new file mode 100644
index 000000000000..b340d7506995
--- /dev/null
+++ b/vcl/osx/a11ywrappertabgroup.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YWRAPPERTABGROUP_H
+#define INCLUDED_VCL_OSX_A11YWRAPPERTABGROUP_H
+
+#include "osx/a11ywrapper.h"
+
+@interface AquaA11yWrapperTabGroup : AquaA11yWrapper
+{
+}
+-(NSArray *)accessibilityAttributeNames;
+@end
+
+#endif // INCLUDED_VCL_OSX_A11YWRAPPERTABGROUP_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrappertabgroup.mm b/vcl/osx/a11ywrappertabgroup.mm
new file mode 100644
index 000000000000..0174e43093e2
--- /dev/null
+++ b/vcl/osx/a11ywrappertabgroup.mm
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/salinst.h"
+#include "a11ywrappertabgroup.h"
+
+// Wrapper for AXTabGroup role
+
+@implementation AquaA11yWrapperTabGroup : AquaA11yWrapper
+
+-(NSArray *)accessibilityAttributeNames {
+ // Default Attributes
+ NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ];
+ // Special Attributes and removing unwanted attributes depending on role
+ [ attributeNames addObjectsFromArray: [ NSArray arrayWithObjects:
+ NSAccessibilityContentsAttribute,
+ NSAccessibilityTabsAttribute,
+ nil ]
+ ];
+ return attributeNames;
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrappertextarea.h b/vcl/osx/a11ywrappertextarea.h
new file mode 100644
index 000000000000..a0cf4f68d6af
--- /dev/null
+++ b/vcl/osx/a11ywrappertextarea.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YWRAPPERTEXTAREA_H
+#define INCLUDED_VCL_OSX_A11YWRAPPERTEXTAREA_H
+
+#include "osx/a11ywrapper.h"
+
+@interface AquaA11yWrapperTextArea : AquaA11yWrapper
+{
+}
+-(NSArray *)accessibilityAttributeNames;
+@end
+
+#endif // INCLUDED_VCL_OSX_A11YWRAPPERTEXTAREA_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrappertextarea.mm b/vcl/osx/a11ywrappertextarea.mm
new file mode 100644
index 000000000000..9b4fbf995733
--- /dev/null
+++ b/vcl/osx/a11ywrappertextarea.mm
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/salinst.h"
+#include "a11ywrappertextarea.h"
+
+// Wrapper for AXTextArea role
+
+@implementation AquaA11yWrapperTextArea : AquaA11yWrapper
+
+-(NSArray *)accessibilityAttributeNames {
+ // Default Attributes
+ NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ];
+ // Special Attributes and removing unwanted attributes depending on role
+ [ attributeNames removeObject: NSAccessibilityTitleAttribute ];
+ [ attributeNames removeObject: NSAccessibilityEnabledAttribute ];
+ [ attributeNames addObject: NSAccessibilityChildrenAttribute ];
+ return attributeNames;
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrappertoolbar.h b/vcl/osx/a11ywrappertoolbar.h
new file mode 100644
index 000000000000..64bd14250e9e
--- /dev/null
+++ b/vcl/osx/a11ywrappertoolbar.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_A11YWRAPPERTOOLBAR_H
+#define INCLUDED_VCL_OSX_A11YWRAPPERTOOLBAR_H
+
+#include "osx/a11ywrapper.h"
+
+@interface AquaA11yWrapperToolbar : AquaA11yWrapper
+{
+}
+-(NSArray *)accessibilityAttributeNames;
+@end
+
+#endif // INCLUDED_VCL_OSX_A11YWRAPPERTOOLBAR_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/a11ywrappertoolbar.mm b/vcl/osx/a11ywrappertoolbar.mm
new file mode 100644
index 000000000000..9636d7882705
--- /dev/null
+++ b/vcl/osx/a11ywrappertoolbar.mm
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/salinst.h"
+#include "a11ywrappertoolbar.h"
+
+// Wrapper for AXToolbar role
+
+@implementation AquaA11yWrapperToolbar : AquaA11yWrapper
+
+-(NSArray *)accessibilityAttributeNames {
+ // Default Attributes
+ NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ];
+ // Special Attributes and removing unwanted attributes depending on role
+ [ attributeNames removeObjectsInArray: [ NSArray arrayWithObjects:
+ NSAccessibilityTitleAttribute,
+ NSAccessibilityEnabledAttribute,
+ nil ]
+ ];
+ return attributeNames;
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/clipboard.cxx b/vcl/osx/clipboard.cxx
new file mode 100644
index 000000000000..72feefe5bdb6
--- /dev/null
+++ b/vcl/osx/clipboard.cxx
@@ -0,0 +1,372 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "clipboard.hxx"
+
+#include "DataFlavorMapping.hxx"
+#include "OSXTransferable.hxx"
+#include <com/sun/star/datatransfer/MimeContentTypeFactory.hpp>
+#include "comphelper/makesequence.hxx"
+#include "comphelper/processfactory.hxx"
+
+#include <boost/assert.hpp>
+
+using namespace com::sun::star::datatransfer;
+using namespace com::sun::star::datatransfer::clipboard;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::uno;
+using namespace cppu;
+using namespace osl;
+using namespace std;
+using namespace comphelper;
+
+
+@implementation EventListener;
+
+-(EventListener*)initWithAquaClipboard: (AquaClipboard*) pcb
+{
+ self = [super init];
+
+ if (self)
+ pAquaClipboard = pcb;
+
+ return self;
+}
+
+-(void)pasteboard:(NSPasteboard*)sender provideDataForType:(NSString*)type
+{
+ if( pAquaClipboard )
+ pAquaClipboard->provideDataForType(sender, type);
+}
+
+-(void)applicationDidBecomeActive:(NSNotification*)aNotification
+{
+ if( pAquaClipboard )
+ pAquaClipboard->applicationDidBecomeActive(aNotification);
+}
+
+-(void)disposing
+{
+ pAquaClipboard = NULL;
+}
+
+@end
+
+
+OUString clipboard_getImplementationName()
+{
+ return OUString("com.sun.star.datatransfer.clipboard.AquaClipboard");
+}
+
+Sequence<OUString> clipboard_getSupportedServiceNames()
+{
+ return makeSequence(OUString("com.sun.star.datatransfer.clipboard.SystemClipboard"));
+}
+
+
+AquaClipboard::AquaClipboard(NSPasteboard* pasteboard, bool bUseSystemPasteboard) :
+ WeakComponentImplHelper3<XSystemClipboard, XFlushableClipboard, XServiceInfo>(m_aMutex),
+ mIsSystemPasteboard(bUseSystemPasteboard)
+{
+ Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
+
+ mrXMimeCntFactory = MimeContentTypeFactory::create(xContext);
+
+ mpDataFlavorMapper = DataFlavorMapperPtr_t(new DataFlavorMapper());
+
+ if (pasteboard != NULL)
+ {
+ mPasteboard = pasteboard;
+ mIsSystemPasteboard = false;
+ }
+ else
+ {
+ mPasteboard = bUseSystemPasteboard ? [NSPasteboard generalPasteboard] :
+ [NSPasteboard pasteboardWithName: NSDragPboard];
+
+ if (mPasteboard == nil)
+ {
+ throw RuntimeException("AquaClipboard: Cannot create Cocoa pasteboard",
+ static_cast<XClipboardEx*>(this));
+ }
+ }
+
+ [mPasteboard retain];
+
+ mEventListener = [[EventListener alloc] initWithAquaClipboard: this];
+
+ if (mEventListener == nil)
+ {
+ [mPasteboard release];
+
+ throw RuntimeException(
+ OUString("AquaClipboard: Cannot create pasteboard change listener"),
+ static_cast<XClipboardEx*>(this));
+ }
+
+ if (mIsSystemPasteboard)
+ {
+ NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
+
+ [notificationCenter addObserver: mEventListener
+ selector: @selector(applicationDidBecomeActive:)
+ name: @"NSApplicationDidBecomeActiveNotification"
+ object: [NSApplication sharedApplication]];
+ }
+
+ mPasteboardChangeCount = [mPasteboard changeCount];
+}
+
+
+AquaClipboard::~AquaClipboard()
+{
+ if (mIsSystemPasteboard)
+ {
+ [[NSNotificationCenter defaultCenter] removeObserver: mEventListener];
+ }
+
+ [mEventListener disposing];
+ [mEventListener release];
+ [mPasteboard release];
+}
+
+
+Reference<XTransferable> SAL_CALL AquaClipboard::getContents() throw(RuntimeException)
+{
+ MutexGuard aGuard(m_aMutex);
+
+ // Shortcut: If we are clipboard owner already we don't need
+ // to drag the data through the system clipboard
+ if (mXClipboardContent.is())
+ {
+ return mXClipboardContent;
+ }
+
+ return Reference<XTransferable>(new OSXTransferable(mrXMimeCntFactory,
+ mpDataFlavorMapper,
+ mPasteboard));
+}
+
+
+void SAL_CALL AquaClipboard::setContents(const Reference<XTransferable>& xTransferable,
+ const Reference<XClipboardOwner>& xClipboardOwner)
+ throw( RuntimeException )
+{
+ NSArray* types = xTransferable.is() ?
+ mpDataFlavorMapper->flavorSequenceToTypesArray(xTransferable->getTransferDataFlavors()) :
+ [NSArray array];
+
+ ClearableMutexGuard aGuard(m_aMutex);
+
+ Reference<XClipboardOwner> oldOwner(mXClipboardOwner);
+ mXClipboardOwner = xClipboardOwner;
+
+ Reference<XTransferable> oldContent(mXClipboardContent);
+ mXClipboardContent = xTransferable;
+
+ mPasteboardChangeCount = [mPasteboard declareTypes: types owner: mEventListener];
+
+ aGuard.clear();
+
+ // if we are already the owner of the clipboard
+ // then fire lost ownership event
+ if (oldOwner.is())
+ {
+ fireLostClipboardOwnershipEvent(oldOwner, oldContent);
+ }
+
+ fireClipboardChangedEvent();
+}
+
+
+OUString SAL_CALL AquaClipboard::getName() throw( RuntimeException )
+{
+ return OUString();
+}
+
+
+sal_Int8 SAL_CALL AquaClipboard::getRenderingCapabilities() throw( RuntimeException )
+{
+ return 0;
+}
+
+
+void SAL_CALL AquaClipboard::addClipboardListener(const Reference< XClipboardListener >& listener)
+ throw( RuntimeException )
+{
+ MutexGuard aGuard(m_aMutex);
+
+ if (!listener.is())
+ throw IllegalArgumentException("empty reference",
+ static_cast<XClipboardEx*>(this), 1);
+
+ mClipboardListeners.push_back(listener);
+}
+
+
+void SAL_CALL AquaClipboard::removeClipboardListener(const Reference< XClipboardListener >& listener)
+ throw( RuntimeException )
+{
+ MutexGuard aGuard(m_aMutex);
+
+ if (!listener.is())
+ throw IllegalArgumentException("empty reference",
+ static_cast<XClipboardEx*>(this), 1);
+
+ mClipboardListeners.remove(listener);
+}
+
+
+void AquaClipboard::applicationDidBecomeActive(NSNotification*)
+{
+ ClearableMutexGuard aGuard(m_aMutex);
+
+ int currentPboardChgCount = [mPasteboard changeCount];
+
+ if (currentPboardChgCount != mPasteboardChangeCount)
+ {
+ mPasteboardChangeCount = currentPboardChgCount;
+
+ // Clear clipboard content and owner and send lostOwnership
+ // notification to the old clipboard owner as well as
+ // ClipboardChanged notification to any clipboard listener
+ Reference<XClipboardOwner> oldOwner(mXClipboardOwner);
+ mXClipboardOwner.clear();
+
+ Reference<XTransferable> oldContent(mXClipboardContent);
+ mXClipboardContent.clear();
+
+ aGuard.clear();
+
+ if (oldOwner.is())
+ {
+ fireLostClipboardOwnershipEvent(oldOwner, oldContent);
+ }
+
+ fireClipboardChangedEvent();
+ }
+}
+
+
+void AquaClipboard::fireClipboardChangedEvent()
+{
+ ClearableMutexGuard aGuard(m_aMutex);
+
+ list<Reference< XClipboardListener > > listeners(mClipboardListeners);
+ ClipboardEvent aEvent;
+
+ if (listeners.begin() != listeners.end())
+ {
+ aEvent = ClipboardEvent(static_cast<OWeakObject*>(this), getContents());
+ }
+
+ aGuard.clear();
+
+ while (listeners.begin() != listeners.end())
+ {
+ if (listeners.front().is())
+ {
+ try { listeners.front()->changedContents(aEvent); }
+ catch (RuntimeException&) { }
+ }
+ listeners.pop_front();
+ }
+}
+
+
+void AquaClipboard::fireLostClipboardOwnershipEvent(Reference<XClipboardOwner> oldOwner, Reference<XTransferable> oldContent)
+{
+ BOOST_ASSERT(oldOwner.is());
+
+ try { oldOwner->lostOwnership(static_cast<XClipboardEx*>(this), oldContent); }
+ catch(RuntimeException&) { }
+}
+
+
+void AquaClipboard::provideDataForType(NSPasteboard* sender, NSString* type)
+{
+ if( mXClipboardContent.is() )
+ {
+ DataProviderPtr_t dp = mpDataFlavorMapper->getDataProvider(type, mXClipboardContent);
+ NSData* pBoardData = NULL;
+
+ if (dp.get() != NULL)
+ {
+ pBoardData = (NSData*)dp->getSystemData();
+ [sender setData: pBoardData forType: type];
+ }
+ }
+}
+
+
+//------------------------------------------------
+// XFlushableClipboard
+//------------------------------------------------
+
+void SAL_CALL AquaClipboard::flushClipboard()
+ throw(RuntimeException)
+{
+ if (mXClipboardContent.is())
+ {
+ Sequence<DataFlavor> flavorList = mXClipboardContent->getTransferDataFlavors();
+ sal_uInt32 nFlavors = flavorList.getLength();
+ bool bInternal(false);
+
+ for (sal_uInt32 i = 0; i < nFlavors; i++)
+ {
+ NSString* sysType = mpDataFlavorMapper->openOfficeToSystemFlavor(flavorList[i], bInternal);
+
+ if (sysType != NULL)
+ {
+ provideDataForType(mPasteboard, sysType);
+ }
+ }
+ mXClipboardContent.clear();
+ }
+}
+
+
+NSPasteboard* AquaClipboard::getPasteboard() const
+{
+ return mPasteboard;
+}
+
+
+//-------------------------------------------------
+// XServiceInfo
+//-------------------------------------------------
+
+OUString SAL_CALL AquaClipboard::getImplementationName() throw( RuntimeException )
+{
+ return clipboard_getImplementationName();
+}
+
+
+sal_Bool SAL_CALL AquaClipboard::supportsService( const OUString& /*ServiceName*/ ) throw( RuntimeException )
+{
+ return sal_False;
+}
+
+
+Sequence< OUString > SAL_CALL AquaClipboard::getSupportedServiceNames() throw( RuntimeException )
+{
+ return clipboard_getSupportedServiceNames();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/clipboard.hxx b/vcl/osx/clipboard.hxx
new file mode 100644
index 000000000000..1424e34fc4d4
--- /dev/null
+++ b/vcl/osx/clipboard.hxx
@@ -0,0 +1,175 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_CLIPBOARD_HXX
+#define INCLUDED_VCL_OSX_CLIPBOARD_HXX
+
+#include "DataFlavorMapping.hxx"
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <cppuhelper/compbase3.hxx>
+#include <com/sun/star/datatransfer/XTransferable.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboardEx.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboardOwner.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboardListener.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboardNotifier.hpp>
+#include <com/sun/star/datatransfer/clipboard/XSystemClipboard.hpp>
+#include <com/sun/star/datatransfer/XMimeContentTypeFactory.hpp>
+#include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <cppuhelper/basemutex.hxx>
+#include <com/sun/star/lang/XMultiComponentFactory.hpp>
+
+#include <boost/utility.hpp>
+#include <list>
+
+#include <premac.h>
+#import <Cocoa/Cocoa.h>
+#include <postmac.h>
+
+class AquaClipboard;
+
+@interface EventListener : NSObject
+{
+ AquaClipboard* pAquaClipboard;
+}
+
+// Init the pasteboard change listener with a reference to the OfficeClipboard
+// instance
+- (EventListener*)initWithAquaClipboard: (AquaClipboard*) pcb;
+
+// Promiss resolver function
+- (void)pasteboard:(NSPasteboard*)sender provideDataForType:(NSString *)type;
+
+-(void)applicationDidBecomeActive:(NSNotification*)aNotification;
+
+-(void)disposing;
+@end
+
+
+class AquaClipboard : public ::cppu::BaseMutex,
+ public ::cppu::WeakComponentImplHelper3< com::sun::star::datatransfer::clipboard::XSystemClipboard,
+ com::sun::star::datatransfer::clipboard::XFlushableClipboard,
+ com::sun::star::lang::XServiceInfo >,
+ private ::boost::noncopyable
+{
+public:
+ /* Create a clipboard instance.
+
+ @param pasteboard
+ If not equal NULL the instance will be instantiated with the provided
+ pasteboard reference and 'bUseSystemClipboard' will be ignored
+
+ @param bUseSystemClipboard
+ If 'pasteboard' is NULL 'bUseSystemClipboard' determines whether the
+ system clipboard will be created (bUseSystemClipboard == true) or if
+ the DragPasteboard if bUseSystemClipboard == false
+ */
+ AquaClipboard(NSPasteboard* pasteboard = NULL,
+ bool bUseSystemClipboard = true);
+
+ ~AquaClipboard();
+
+ //------------------------------------------------
+ // XClipboard
+ //------------------------------------------------
+
+ virtual ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XTransferable > SAL_CALL getContents()
+ throw( ::com::sun::star::uno::RuntimeException );
+
+ virtual void SAL_CALL setContents( const ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XTransferable >& xTransferable,
+ const ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::clipboard::XClipboardOwner >& xClipboardOwner )
+ throw( ::com::sun::star::uno::RuntimeException );
+
+ virtual OUString SAL_CALL getName()
+ throw( ::com::sun::star::uno::RuntimeException );
+
+ //------------------------------------------------
+ // XClipboardEx
+ //------------------------------------------------
+
+ virtual sal_Int8 SAL_CALL getRenderingCapabilities()
+ throw( ::com::sun::star::uno::RuntimeException );
+
+ //------------------------------------------------
+ // XClipboardNotifier
+ //------------------------------------------------
+
+ virtual void SAL_CALL addClipboardListener( const ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::clipboard::XClipboardListener >& listener )
+ throw( ::com::sun::star::uno::RuntimeException );
+
+ virtual void SAL_CALL removeClipboardListener( const ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::clipboard::XClipboardListener >& listener )
+ throw( ::com::sun::star::uno::RuntimeException );
+
+ //------------------------------------------------
+ // XFlushableClipboard
+ //------------------------------------------------
+
+ virtual void SAL_CALL flushClipboard( ) throw( com::sun::star::uno::RuntimeException );
+
+ //------------------------------------------------
+ // XServiceInfo
+ //------------------------------------------------
+
+ virtual OUString SAL_CALL getImplementationName()
+ throw(::com::sun::star::uno::RuntimeException);
+
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName )
+ throw(::com::sun::star::uno::RuntimeException);
+
+ virtual ::com::sun::star::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames()
+ throw(::com::sun::star::uno::RuntimeException);
+
+ /* Get a reference to the used pastboard.
+ */
+ NSPasteboard* getPasteboard() const;
+
+ /* Notify the current clipboard owner that he is no longer the clipboard owner.
+ */
+ void fireLostClipboardOwnershipEvent(::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::clipboard::XClipboardOwner> oldOwner,
+ ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XTransferable > oldContent);
+
+ void pasteboardChangedOwner();
+
+ void provideDataForType(NSPasteboard* sender, NSString* type);
+
+ void applicationDidBecomeActive(NSNotification* aNotification);
+
+private:
+
+ /* Notify all registered XClipboardListener that the clipboard content
+ has changed.
+ */
+ void fireClipboardChangedEvent();
+
+private:
+ ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XMimeContentTypeFactory > mrXMimeCntFactory;
+ ::std::list< ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::clipboard::XClipboardListener > > mClipboardListeners;
+ ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XTransferable > mXClipboardContent;
+ com::sun::star::uno::Reference< com::sun::star::datatransfer::clipboard::XClipboardOwner > mXClipboardOwner;
+ DataFlavorMapperPtr_t mpDataFlavorMapper;
+ bool mIsSystemPasteboard;
+ NSPasteboard* mPasteboard;
+ int mPasteboardChangeCount;
+ EventListener* mEventListener;
+};
+
+#endif // INCLUDED_VCL_OSX_CLIPBOARD_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/documentfocuslistener.cxx b/vcl/osx/documentfocuslistener.cxx
new file mode 100644
index 000000000000..ac65f1e84338
--- /dev/null
+++ b/vcl/osx/documentfocuslistener.cxx
@@ -0,0 +1,241 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "documentfocuslistener.hxx"
+
+#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
+
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+
+
+//------------------------------------------------------------------------------
+
+DocumentFocusListener::DocumentFocusListener(AquaA11yFocusTracker& rTracker) :
+ m_aFocusTracker(rTracker)
+{
+}
+
+//------------------------------------------------------------------------------
+
+void SAL_CALL
+DocumentFocusListener::disposing( const EventObject& aEvent )
+ throw (RuntimeException)
+{
+ // Unref the object here, but do not remove as listener since the object
+ // might no longer be in a state that safely allows this.
+ if( aEvent.Source.is() )
+ m_aRefList.erase(aEvent.Source);
+}
+
+//------------------------------------------------------------------------------
+
+void SAL_CALL
+DocumentFocusListener::notifyEvent( const AccessibleEventObject& aEvent )
+ throw( RuntimeException )
+{
+ switch( aEvent.EventId )
+ {
+ case AccessibleEventId::STATE_CHANGED:
+ try
+ {
+ sal_Int16 nState = AccessibleStateType::INVALID;
+ aEvent.NewValue >>= nState;
+
+ if( AccessibleStateType::FOCUSED == nState )
+ m_aFocusTracker.setFocusedObject( getAccessible(aEvent) );
+ }
+ catch(const IndexOutOfBoundsException &)
+ {
+ OSL_TRACE("Focused object has invalid index in parent");
+ }
+ break;
+
+ case AccessibleEventId::CHILD:
+ {
+ Reference< XAccessible > xChild;
+ if( (aEvent.OldValue >>= xChild) && xChild.is() )
+ detachRecursive(xChild);
+
+ if( (aEvent.NewValue >>= xChild) && xChild.is() )
+ attachRecursive(xChild);
+ }
+ break;
+
+ case AccessibleEventId::INVALIDATE_ALL_CHILDREN:
+ {
+ Reference< XAccessible > xAccessible( getAccessible(aEvent) );
+ detachRecursive(xAccessible);
+ attachRecursive(xAccessible);
+ }
+
+ OSL_TRACE( "Invalidate all children called" );
+ break;
+ default:
+ break;
+ }
+}
+
+//------------------------------------------------------------------------------
+
+Reference< XAccessible > DocumentFocusListener::getAccessible(const EventObject& aEvent )
+ throw (IndexOutOfBoundsException, RuntimeException)
+{
+ Reference< XAccessible > xAccessible(aEvent.Source, UNO_QUERY);
+
+ if( xAccessible.is() )
+ return xAccessible;
+
+ Reference< XAccessibleContext > xContext(aEvent.Source, UNO_QUERY);
+
+ if( xContext.is() )
+ {
+ Reference< XAccessible > xParent( xContext->getAccessibleParent() );
+ if( xParent.is() )
+ {
+ Reference< XAccessibleContext > xParentContext( xParent->getAccessibleContext() );
+ if( xParentContext.is() )
+ {
+ return xParentContext->getAccessibleChild( xContext->getAccessibleIndexInParent() );
+ }
+ }
+ }
+
+ return Reference< XAccessible >();
+}
+
+//------------------------------------------------------------------------------
+
+void DocumentFocusListener::attachRecursive(const Reference< XAccessible >& xAccessible)
+ throw (IndexOutOfBoundsException, RuntimeException)
+{
+ Reference< XAccessibleContext > xContext = xAccessible->getAccessibleContext();
+
+ if( xContext.is() )
+ attachRecursive(xAccessible, xContext);
+}
+
+//------------------------------------------------------------------------------
+
+void DocumentFocusListener::attachRecursive(
+ const Reference< XAccessible >& xAccessible,
+ const Reference< XAccessibleContext >& xContext
+) throw (IndexOutOfBoundsException, RuntimeException)
+{
+ if( xContext.is() )
+ {
+ Reference< XAccessibleStateSet > xStateSet = xContext->getAccessibleStateSet();
+
+ if( xStateSet.is() )
+ attachRecursive(xAccessible, xContext, xStateSet);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+void DocumentFocusListener::attachRecursive(
+ const Reference< XAccessible >& xAccessible,
+ const Reference< XAccessibleContext >& xContext,
+ const Reference< XAccessibleStateSet >& xStateSet
+) throw (IndexOutOfBoundsException,RuntimeException)
+{
+ if( xStateSet->contains(AccessibleStateType::FOCUSED ) )
+ m_aFocusTracker.setFocusedObject( xAccessible );
+
+ Reference< XAccessibleEventBroadcaster > xBroadcaster =
+ Reference< XAccessibleEventBroadcaster >(xContext, UNO_QUERY);
+
+ // If not already done, add the broadcaster to the list and attach as listener.
+ if( xBroadcaster.is() && m_aRefList.insert(xBroadcaster).second )
+ {
+ xBroadcaster->addAccessibleEventListener(static_cast< XAccessibleEventListener *>(this));
+
+ if( ! xStateSet->contains(AccessibleStateType::MANAGES_DESCENDANTS ) )
+ {
+ sal_Int32 n, nmax = xContext->getAccessibleChildCount();
+ for( n = 0; n < nmax; n++ )
+ {
+ Reference< XAccessible > xChild( xContext->getAccessibleChild( n ) );
+
+ if( xChild.is() )
+ attachRecursive(xChild);
+ }
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+
+void DocumentFocusListener::detachRecursive(const Reference< XAccessible >& xAccessible)
+ throw (IndexOutOfBoundsException, RuntimeException)
+{
+ Reference< XAccessibleContext > xContext = xAccessible->getAccessibleContext();
+
+ if( xContext.is() )
+ detachRecursive(xAccessible, xContext);
+}
+
+//------------------------------------------------------------------------------
+
+void DocumentFocusListener::detachRecursive(
+ const Reference< XAccessible >& xAccessible,
+ const Reference< XAccessibleContext >& xContext
+) throw (IndexOutOfBoundsException, RuntimeException)
+{
+ Reference< XAccessibleStateSet > xStateSet = xContext->getAccessibleStateSet();
+
+ if( xStateSet.is() )
+ detachRecursive(xAccessible, xContext, xStateSet);
+}
+
+//------------------------------------------------------------------------------
+
+void DocumentFocusListener::detachRecursive(
+ const Reference< XAccessible >&,
+ const Reference< XAccessibleContext >& xContext,
+ const Reference< XAccessibleStateSet >& xStateSet
+) throw (IndexOutOfBoundsException, RuntimeException)
+{
+ Reference< XAccessibleEventBroadcaster > xBroadcaster =
+ Reference< XAccessibleEventBroadcaster >(xContext, UNO_QUERY);
+
+ if( xBroadcaster.is() && 0 < m_aRefList.erase(xBroadcaster) )
+ {
+ xBroadcaster->removeAccessibleEventListener(static_cast< XAccessibleEventListener *>(this));
+
+ if( ! xStateSet->contains(AccessibleStateType::MANAGES_DESCENDANTS ) )
+ {
+ sal_Int32 n, nmax = xContext->getAccessibleChildCount();
+ for( n = 0; n < nmax; n++ )
+ {
+ Reference< XAccessible > xChild( xContext->getAccessibleChild( n ) );
+
+ if( xChild.is() )
+ detachRecursive(xChild);
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/documentfocuslistener.hxx b/vcl/osx/documentfocuslistener.hxx
new file mode 100644
index 000000000000..1a604a677df5
--- /dev/null
+++ b/vcl/osx/documentfocuslistener.hxx
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_OSX_DOCUMENTFOCUSLISTENER_HXX
+#define INCLUDED_VCL_OSX_DOCUMENTFOCUSLISTENER_HXX
+
+#include <com/sun/star/accessibility/XAccessibleEventListener.hpp>
+
+#include <cppuhelper/implbase1.hxx>
+
+#include "osx/a11yfocustracker.hxx"
+
+#include <set>
+
+// -------------------------
+// - DocumentFocusListener -
+// -------------------------
+
+class DocumentFocusListener :
+ public ::cppu::WeakImplHelper1< ::com::sun::star::accessibility::XAccessibleEventListener >
+{
+
+public:
+
+ DocumentFocusListener(AquaA11yFocusTracker& rTracker);
+
+ void attachRecursive(
+ const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& xAccessible
+ ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException);
+
+ void attachRecursive(
+ const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& xAccessible,
+ const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleContext >& xContext
+ ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException);
+
+ void attachRecursive(
+ const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& xAccessible,
+ const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleContext >& xContext,
+ const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleStateSet >& xStateSet
+ ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException);
+
+ void detachRecursive(
+ const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& xAccessible
+ ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException);
+
+ void detachRecursive(
+ const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& xAccessible,
+ const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleContext >& xContext
+ ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException);
+
+ void detachRecursive(
+ const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& xAccessible,
+ const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleContext >& xContext,
+ const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleStateSet >& xStateSet
+ ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException);
+
+ static ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > getAccessible(const ::com::sun::star::lang::EventObject& aEvent )
+ throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException);
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const ::com::sun::star::lang::EventObject& Source )
+ throw (::com::sun::star::uno::RuntimeException);
+
+ // XAccessibleEventListener
+ virtual void SAL_CALL notifyEvent( const ::com::sun::star::accessibility::AccessibleEventObject& aEvent )
+ throw( ::com::sun::star::uno::RuntimeException );
+
+private:
+ std::set< ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > > m_aRefList;
+
+ AquaA11yFocusTracker& m_aFocusTracker;
+};
+
+#endif // INCLUDED_VCL_OSX_DOCUMENTFOCUSLISTENER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/printaccessoryview.mm b/vcl/osx/printaccessoryview.mm
new file mode 100644
index 000000000000..517dcc09975f
--- /dev/null
+++ b/vcl/osx/printaccessoryview.mm
@@ -0,0 +1,1381 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "sal/config.h"
+
+#include "tools/resary.hxx"
+
+#include "vcl/print.hxx"
+#include "vcl/image.hxx"
+#include "vcl/virdev.hxx"
+#include "vcl/svapp.hxx"
+#include "vcl/unohelp.hxx"
+
+#include "osx/printview.h"
+#include "osx/salinst.h"
+#include "quartz/utils.h"
+
+#include "svdata.hxx"
+#include "svids.hrc"
+
+#include "com/sun/star/i18n/XBreakIterator.hpp"
+#include "com/sun/star/i18n/WordType.hpp"
+
+#include <map>
+
+using namespace vcl;
+using namespace com::sun::star;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::uno;
+
+/* Note: the accesory view as implemented here is already deprecated in Leopard. Unfortunately
+ as long as our baseline is Tiger we cannot gain the advantages over multiple accessory views
+ as well havs having accessory views AND a preview (as long as you are linked vs. 10.4 libraries
+ the preview insists on not being present. This is unfortunate.
+*/
+
+class ControllerProperties;
+
+@interface ControlTarget : NSObject
+{
+ ControllerProperties* mpController;
+}
+-(id)initWithControllerMap: (ControllerProperties*)pController;
+-(void)triggered:(id)pSender;
+-(void)triggeredNumeric:(id)pSender;
+-(void)triggeredPreview:(id)pSender;
+-(void)dealloc;
+@end
+
+
+class ControllerProperties
+{
+ vcl::PrinterController* mpController;
+ std::map< int, rtl::OUString > maTagToPropertyName;
+ std::map< int, sal_Int32 > maTagToValueInt;
+ std::map< NSView*, NSView* > maViewPairMap;
+ std::vector< NSObject* > maViews;
+ int mnNextTag;
+ sal_Int32 mnLastPageCount;
+ PrintAccessoryViewState* mpState;
+ NSPrintOperation* mpOp;
+ NSView* mpAccessoryView;
+ NSTabView* mpTabView;
+ NSBox* mpPreviewBox;
+ NSImageView* mpPreview;
+ NSTextField* mpPageEdit;
+ NSStepper* mpStepper;
+ NSTextView* mpPagesLabel;
+ ResStringArray maLocalizedStrings;
+
+ public:
+ ControllerProperties( vcl::PrinterController* i_pController,
+ NSPrintOperation* i_pOp,
+ NSView* i_pAccessoryView,
+ NSTabView* i_pTabView,
+ PrintAccessoryViewState* i_pState )
+ : mpController( i_pController ),
+ mnNextTag( 0 ),
+ mnLastPageCount( i_pController->getFilteredPageCount() ),
+ mpState( i_pState ),
+ mpOp( i_pOp ),
+ mpAccessoryView( i_pAccessoryView ),
+ mpTabView( i_pTabView ),
+ mpPreviewBox( nil ),
+ mpPreview( nil ),
+ mpPageEdit( nil ),
+ mpStepper( nil ),
+ mpPagesLabel( nil ),
+ maLocalizedStrings( VclResId( SV_PRINT_NATIVE_STRINGS ) )
+ {
+ mpState->bNeedRestart = false;
+ DBG_ASSERT( maLocalizedStrings.Count() >= 5, "resources not found !" );
+ }
+
+ rtl::OUString getMoreString()
+ {
+ return maLocalizedStrings.Count() >= 4
+ ? OUString( maLocalizedStrings.GetString( 3 ) )
+ : OUString( "More" );
+ }
+
+ rtl::OUString getPrintSelectionString()
+ {
+ return maLocalizedStrings.Count() >= 5
+ ? OUString( maLocalizedStrings.GetString( 4 ) )
+ : OUString( "Print selection only" );
+ }
+
+ void updatePrintJob()
+ {
+ // TODO: refresh page count etc from mpController
+
+ // page range may have changed depending on options
+ sal_Int32 nPages = mpController->getFilteredPageCount();
+ #if OSL_DEBUG_LEVEL > 1
+ if( nPages != mnLastPageCount )
+ fprintf( stderr, "trouble: number of pages changed from %ld to %ld !\n", mnLastPageCount, nPages );
+ #endif
+ mpState->bNeedRestart = (nPages != mnLastPageCount);
+ NSTabViewItem* pItem = [mpTabView selectedTabViewItem];
+ if( pItem )
+ mpState->nLastPage = [mpTabView indexOfTabViewItem: pItem];
+ else
+ mpState->nLastPage = 0;
+ mnLastPageCount = nPages;
+ if( mpState->bNeedRestart )
+ {
+ // Warning: bad hack ahead
+ // Apple does not give us a chance of changing the page count,
+ // and they don't let us cancel the dialog either
+ // hack: send a cancel message to the window displaying our views.
+ // this is ugly.
+ NSWindow* pNSWindow = [NSApp modalWindow];
+ if( pNSWindow )
+ [pNSWindow cancelOperation: nil];
+ [[mpOp printInfo] setJobDisposition: NSPrintCancelJob];
+ }
+ else
+ {
+ sal_Int32 nPage = [mpStepper intValue];
+ updatePreviewImage( nPage-1 );
+ }
+ }
+
+ int addNameTag( const rtl::OUString& i_rPropertyName )
+ {
+ int nNewTag = mnNextTag++;
+ maTagToPropertyName[ nNewTag ] = i_rPropertyName;
+ return nNewTag;
+ }
+
+ int addNameAndValueTag( const rtl::OUString& i_rPropertyName, sal_Int32 i_nValue )
+ {
+ int nNewTag = mnNextTag++;
+ maTagToPropertyName[ nNewTag ] = i_rPropertyName;
+ maTagToValueInt[ nNewTag ] = i_nValue;
+ return nNewTag;
+ }
+
+ void addObservedControl( NSObject* i_pView )
+ {
+ maViews.push_back( i_pView );
+ }
+
+ void addViewPair( NSView* i_pLeft, NSView* i_pRight )
+ {
+ maViewPairMap[ i_pLeft ] = i_pRight;
+ maViewPairMap[ i_pRight ] = i_pLeft;
+ }
+
+ NSView* getPair( NSView* i_pLeft ) const
+ {
+ NSView* pRight = nil;
+ std::map< NSView*, NSView* >::const_iterator it = maViewPairMap.find( i_pLeft );
+ if( it != maViewPairMap.end() )
+ pRight = it->second;
+ return pRight;
+ }
+
+ void changePropertyWithIntValue( int i_nTag )
+ {
+ std::map< int, rtl::OUString >::const_iterator name_it = maTagToPropertyName.find( i_nTag );
+ std::map< int, sal_Int32 >::const_iterator value_it = maTagToValueInt.find( i_nTag );
+ if( name_it != maTagToPropertyName.end() && value_it != maTagToValueInt.end() )
+ {
+ PropertyValue* pVal = mpController->getValue( name_it->second );
+ if( pVal )
+ {
+ pVal->Value <<= value_it->second;
+ updatePrintJob();
+ }
+ }
+ }
+
+ void changePropertyWithIntValue( int i_nTag, sal_Int64 i_nValue )
+ {
+ std::map< int, rtl::OUString >::const_iterator name_it = maTagToPropertyName.find( i_nTag );
+ if( name_it != maTagToPropertyName.end() )
+ {
+ PropertyValue* pVal = mpController->getValue( name_it->second );
+ if( pVal )
+ {
+ pVal->Value <<= i_nValue;
+ updatePrintJob();
+ }
+ }
+ }
+
+ void changePropertyWithBoolValue( int i_nTag, sal_Bool i_bValue )
+ {
+ std::map< int, rtl::OUString >::const_iterator name_it = maTagToPropertyName.find( i_nTag );
+ if( name_it != maTagToPropertyName.end() )
+ {
+ PropertyValue* pVal = mpController->getValue( name_it->second );
+ if( pVal )
+ {
+ // ugly
+ if( name_it->second.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("PrintContent")) )
+ pVal->Value <<= i_bValue ? sal_Int32(2) : sal_Int32(0);
+ else
+ pVal->Value <<= i_bValue;
+ updatePrintJob();
+ }
+ }
+ }
+
+ void changePropertyWithStringValue( int i_nTag, const rtl::OUString& i_rValue )
+ {
+ std::map< int, rtl::OUString >::const_iterator name_it = maTagToPropertyName.find( i_nTag );
+ if( name_it != maTagToPropertyName.end() )
+ {
+ PropertyValue* pVal = mpController->getValue( name_it->second );
+ if( pVal )
+ {
+ pVal->Value <<= i_rValue;
+ updatePrintJob();
+ }
+ }
+ }
+
+ void updateEnableState()
+ {
+ for( std::vector< NSObject* >::iterator it = maViews.begin(); it != maViews.end(); ++it )
+ {
+ NSObject* pObj = *it;
+ NSControl* pCtrl = nil;
+ NSCell* pCell = nil;
+ if( [pObj isKindOfClass: [NSControl class]] )
+ pCtrl = (NSControl*)pObj;
+ else if( [pObj isKindOfClass: [NSCell class]] )
+ pCell = (NSCell*)pObj;
+
+ int nTag = pCtrl ? [pCtrl tag] :
+ pCell ? [pCell tag] :
+ -1;
+
+ std::map< int, rtl::OUString >::const_iterator name_it = maTagToPropertyName.find( nTag );
+ if( name_it != maTagToPropertyName.end() && ! name_it->second.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("PrintContent")) )
+ {
+ BOOL bEnabled = mpController->isUIOptionEnabled( name_it->second ) ? YES : NO;
+ if( pCtrl )
+ {
+ [pCtrl setEnabled: bEnabled];
+ NSView* pOther = getPair( pCtrl );
+ if( pOther && [pOther isKindOfClass: [NSControl class]] )
+ [(NSControl*)pOther setEnabled: bEnabled];
+ }
+ else if( pCell )
+ [pCell setEnabled: bEnabled];
+
+ }
+ }
+ }
+
+ void updatePreviewImage( sal_Int32 i_nPage )
+ {
+ sal_Int32 nPages = mpController->getFilteredPageCount();
+ NSRect aViewFrame = [mpPreview frame];
+ Size aPixelSize( static_cast<long>(aViewFrame.size.width),
+ static_cast<long>(aViewFrame.size.height) );
+ if( i_nPage >= 0 && nPages > i_nPage )
+ {
+ GDIMetaFile aMtf;
+ PrinterController::PageSize aPageSize( mpController->getFilteredPageFile( i_nPage, aMtf, false ) );
+ VirtualDevice aDev;
+ if( mpController->getPrinter()->GetPrinterOptions().IsConvertToGreyscales() )
+ aDev.SetDrawMode( aDev.GetDrawMode() | ( DRAWMODE_GRAYLINE | DRAWMODE_GRAYFILL | DRAWMODE_GRAYTEXT |
+ DRAWMODE_GRAYBITMAP | DRAWMODE_GRAYGRADIENT ) );
+ // see salprn.cxx, currently we pretend to be a 720dpi device on printers
+ aDev.SetReferenceDevice( 720, 720 );
+ aDev.EnableOutput( TRUE );
+ Size aLogicSize( aDev.PixelToLogic( aPixelSize, MapMode( MAP_100TH_MM ) ) );
+ double fScaleX = double(aLogicSize.Width())/double(aPageSize.aSize.Width());
+ double fScaleY = double(aLogicSize.Height())/double(aPageSize.aSize.Height());
+ double fScale = (fScaleX < fScaleY) ? fScaleX : fScaleY;
+ // #i104784# if we render the page too small then rounding issues result in
+ // layout artifacts looking really bad. So scale the page unto a device that is not
+ // full page size but not too small either. This also results in much better visual
+ // quality of the preview, e.g. when its height approaches the number of text lines
+ if( fScale < 0.1 )
+ fScale = 0.1;
+ aMtf.WindStart();
+ aMtf.Scale( fScale, fScale );
+ aMtf.WindStart();
+ aLogicSize.Width() = long(double(aPageSize.aSize.Width()) * fScale);
+ aLogicSize.Height() = long(double(aPageSize.aSize.Height()) * fScale);
+ aPixelSize = aDev.LogicToPixel( aLogicSize, MapMode( MAP_100TH_MM ) );
+ aDev.SetOutputSizePixel( aPixelSize );
+ aMtf.WindStart();
+ aDev.SetMapMode( MapMode( MAP_100TH_MM ) );
+ aMtf.Play( &aDev, Point( 0, 0 ), aLogicSize );
+ aDev.EnableMapMode( FALSE );
+ Image aImage( aDev.GetBitmap( Point( 0, 0 ), aPixelSize ) );
+ NSImage* pImage = CreateNSImage( aImage );
+ [mpPreview setImage: [pImage autorelease]];
+ }
+ else
+ [mpPreview setImage: nil];
+ }
+
+ void setupPreview( ControlTarget* i_pCtrlTarget )
+ {
+ if( maLocalizedStrings.Count() < 3 )
+ return;
+
+ // get the preview control
+ NSRect aPreviewFrame = [mpAccessoryView frame];
+ aPreviewFrame.origin.x = 0;
+ aPreviewFrame.origin.y = 5;
+ aPreviewFrame.size.width = 190;
+ aPreviewFrame.size.height -= 7;
+
+ // create a box to put the preview controls in
+ mpPreviewBox = [[NSBox alloc] initWithFrame: aPreviewFrame];
+ [mpPreviewBox setTitle: [CreateNSString( maLocalizedStrings.GetString( 0 ) ) autorelease]];
+ [mpAccessoryView addSubview: [mpPreviewBox autorelease]];
+
+ // now create the image view of the preview
+ NSSize aMargins = [mpPreviewBox contentViewMargins];
+ aPreviewFrame.origin.x = 0;
+ aPreviewFrame.origin.y = 34;
+ aPreviewFrame.size.width -= 2*(aMargins.width+1);
+ aPreviewFrame.size.height -= 61;
+ mpPreview = [[NSImageView alloc] initWithFrame: aPreviewFrame];
+ [mpPreview setImageScaling: NSScaleProportionally];
+ [mpPreview setImageAlignment: NSImageAlignCenter];
+ [mpPreview setImageFrameStyle: NSImageFrameNone];
+ [mpPreviewBox addSubview: [mpPreview autorelease]];
+
+ // add a label
+ sal_Int32 nPages = mpController->getFilteredPageCount();
+ rtl::OUStringBuffer aBuf( 16 );
+ aBuf.appendAscii( "/ " );
+ aBuf.append( rtl::OUString::number( nPages ) );
+
+ NSString* pText = CreateNSString( aBuf.makeStringAndClear() );
+ NSRect aTextRect = { { 100, 5 }, { 100, 22 } };
+ mpPagesLabel = [[NSTextView alloc] initWithFrame: aTextRect];
+ [mpPagesLabel setFont: [NSFont controlContentFontOfSize: 0]];
+ [mpPagesLabel setEditable: NO];
+ [mpPagesLabel setSelectable: NO];
+ [mpPagesLabel setDrawsBackground: NO];
+ [mpPagesLabel setString: [pText autorelease]];
+ [mpPagesLabel setToolTip: [CreateNSString( maLocalizedStrings.GetString( 2 ) ) autorelease]];
+ [mpPreviewBox addSubview: [mpPagesLabel autorelease]];
+
+ NSRect aFieldRect = { { 45, 5 }, { 35, 25 } };
+ mpPageEdit = [[NSTextField alloc] initWithFrame: aFieldRect];
+ [mpPageEdit setEditable: YES];
+ [mpPageEdit setSelectable: YES];
+ [mpPageEdit setDrawsBackground: YES];
+ [mpPageEdit setToolTip: [CreateNSString( maLocalizedStrings.GetString( 1 ) ) autorelease]];
+ [mpPreviewBox addSubview: [mpPageEdit autorelease]];
+
+ // add a stepper control
+ NSRect aStepFrame = { { 85, 5 }, { 15, 25 } };
+ mpStepper = [[NSStepper alloc] initWithFrame: aStepFrame];
+ [mpStepper setIncrement: 1];
+ [mpStepper setValueWraps: NO];
+ [mpPreviewBox addSubview: [mpStepper autorelease]];
+
+ // constrain the text field to decimal numbers
+ NSNumberFormatter* pFormatter = [[NSNumberFormatter alloc] init];
+ [pFormatter setFormatterBehavior: NSNumberFormatterBehavior10_4];
+ [pFormatter setMinimum: [[NSNumber numberWithInt: 1] autorelease]];
+ [pFormatter setMaximum: [[NSNumber numberWithInt: nPages] autorelease]];
+ [pFormatter setNumberStyle: NSNumberFormatterDecimalStyle];
+ [pFormatter setAllowsFloats: NO];
+ [pFormatter setMaximumFractionDigits: 0];
+ [mpPageEdit setFormatter: pFormatter];
+ [mpStepper setMinValue: 1];
+ [mpStepper setMaxValue: nPages];
+
+ [mpPageEdit setIntValue: 1];
+ [mpStepper setIntValue: 1];
+
+ // connect target and action
+ [mpStepper setTarget: i_pCtrlTarget];
+ [mpStepper setAction: @selector(triggeredPreview:)];
+ [mpPageEdit setTarget: i_pCtrlTarget];
+ [mpPageEdit setAction: @selector(triggeredPreview:)];
+
+ // set first preview image
+ updatePreviewImage( 0 );
+ }
+
+ void changePreview( NSObject* i_pSender )
+ {
+ if( [i_pSender isMemberOfClass: [NSTextField class]] )
+ {
+ NSTextField* pField = (NSTextField*)i_pSender;
+ if( pField == mpPageEdit ) // sanity check
+ {
+ sal_Int32 nPage = [pField intValue];
+ [mpStepper setIntValue: nPage];
+ updatePreviewImage( nPage-1 );
+ }
+ }
+ else if( [i_pSender isMemberOfClass: [NSStepper class]] )
+ {
+ NSStepper* pStepper = (NSStepper*)i_pSender;
+ if( pStepper == mpStepper ) // sanity check
+ {
+ sal_Int32 nPage = [pStepper intValue];
+ [mpPageEdit setIntValue: nPage];
+ updatePreviewImage( nPage-1 );
+ }
+ }
+ }
+};
+
+static void filterAccelerator( rtl::OUString& io_rText )
+{
+ rtl::OUStringBuffer aBuf( io_rText.getLength() );
+ for( sal_Int32 nIndex = 0; nIndex != -1; )
+ aBuf.append( io_rText.getToken( 0, '~', nIndex ) );
+ io_rText = aBuf.makeStringAndClear();
+}
+
+@implementation ControlTarget
+-(id)initWithControllerMap: (ControllerProperties*)pController
+{
+ if( (self = [super init]) )
+ {
+ mpController = pController;
+ }
+ return self;
+}
+-(void)triggered:(id)pSender
+{
+ if( [pSender isMemberOfClass: [NSPopUpButton class]] )
+ {
+ NSPopUpButton* pBtn = (NSPopUpButton*)pSender;
+ NSMenuItem* pSelected = [pBtn selectedItem];
+ if( pSelected )
+ {
+ int nTag = [pSelected tag];
+ mpController->changePropertyWithIntValue( nTag );
+ }
+ }
+ else if( [pSender isMemberOfClass: [NSButton class]] )
+ {
+ NSButton* pBtn = (NSButton*)pSender;
+ int nTag = [pBtn tag];
+ mpController->changePropertyWithBoolValue( nTag, [pBtn state] == NSOnState );
+ }
+ else if( [pSender isMemberOfClass: [NSMatrix class]] )
+ {
+ NSObject* pObj = [(NSMatrix*)pSender selectedCell];
+ if( [pObj isMemberOfClass: [NSButtonCell class]] )
+ {
+ NSButtonCell* pCell = (NSButtonCell*)pObj;
+ int nTag = [pCell tag];
+ mpController->changePropertyWithIntValue( nTag );
+ }
+ }
+ else if( [pSender isMemberOfClass: [NSTextField class]] )
+ {
+ NSTextField* pField = (NSTextField*)pSender;
+ int nTag = [pField tag];
+ rtl::OUString aValue = GetOUString( [pSender stringValue] );
+ mpController->changePropertyWithStringValue( nTag, aValue );
+ }
+ else
+ {
+ SAL_INFO( "vcl.osx.print", "Unsupported class" << ([pSender class] ? [NSStringFromClass([pSender class]) UTF8String] : "nil"));
+ }
+ mpController->updateEnableState();
+}
+-(void)triggeredNumeric:(id)pSender
+{
+ if( [pSender isMemberOfClass: [NSTextField class]] )
+ {
+ NSTextField* pField = (NSTextField*)pSender;
+ int nTag = [pField tag];
+ sal_Int64 nValue = [pField intValue];
+
+ NSView* pOther = mpController->getPair( pField );
+ if( pOther )
+ [(NSControl*)pOther setIntValue: nValue];
+
+ mpController->changePropertyWithIntValue( nTag, nValue );
+ }
+ else if( [pSender isMemberOfClass: [NSStepper class]] )
+ {
+ NSStepper* pStep = (NSStepper*)pSender;
+ int nTag = [pStep tag];
+ sal_Int64 nValue = [pStep intValue];
+
+ NSView* pOther = mpController->getPair( pStep );
+ if( pOther )
+ [(NSControl*)pOther setIntValue: nValue];
+
+ mpController->changePropertyWithIntValue( nTag, nValue );
+ }
+ else
+ {
+ SAL_INFO( "vcl.osx.print", "Unsupported class" << ([pSender class] ? [NSStringFromClass([pSender class]) UTF8String] : "nil"));
+ }
+ mpController->updateEnableState();
+}
+-(void)triggeredPreview:(id)pSender
+{
+ mpController->changePreview( pSender );
+}
+-(void)dealloc
+{
+ delete mpController;
+ [super dealloc];
+}
+@end
+
+struct ColumnItem
+{
+ NSControl* pControl;
+ long nOffset;
+ NSControl* pSubControl;
+
+ ColumnItem( NSControl* i_pControl = nil, long i_nOffset = 0, NSControl* i_pSub = nil )
+ : pControl( i_pControl )
+ , nOffset( i_nOffset )
+ , pSubControl( i_pSub )
+ {}
+
+ long getWidth() const
+ {
+ long nWidth = 0;
+ if( pControl )
+ {
+ NSRect aCtrlRect = [pControl frame];
+ nWidth = aCtrlRect.size.width;
+ nWidth += nOffset;
+ if( pSubControl )
+ {
+ NSRect aSubRect = [pSubControl frame];
+ nWidth += aSubRect.size.width;
+ nWidth += aSubRect.origin.x - (aCtrlRect.origin.x + aCtrlRect.size.width);
+ }
+ }
+ return nWidth;
+ }
+};
+
+static void adjustViewAndChildren( NSView* pNSView, NSSize& rMaxSize,
+ std::vector< ColumnItem >& rLeftColumn,
+ std::vector< ColumnItem >& rRightColumn
+ )
+{
+ // balance columns
+
+ // first get overall column widths
+ long nLeftWidth = 0;
+ long nRightWidth = 0;
+ for( size_t i = 0; i < rLeftColumn.size(); i++ )
+ {
+ long nW = rLeftColumn[i].getWidth();
+ if( nW > nLeftWidth )
+ nLeftWidth = nW;
+ }
+ for( size_t i = 0; i < rRightColumn.size(); i++ )
+ {
+ long nW = rRightColumn[i].getWidth();
+ if( nW > nRightWidth )
+ nRightWidth = nW;
+ }
+
+ // right align left column
+ for( size_t i = 0; i < rLeftColumn.size(); i++ )
+ {
+ if( rLeftColumn[i].pControl )
+ {
+ NSRect aCtrlRect = [rLeftColumn[i].pControl frame];
+ long nX = nLeftWidth - aCtrlRect.size.width;
+ if( rLeftColumn[i].pSubControl )
+ {
+ NSRect aSubRect = [rLeftColumn[i].pSubControl frame];
+ nX -= aSubRect.size.width + (aSubRect.origin.x - (aCtrlRect.origin.x + aCtrlRect.size.width));
+ aSubRect.origin.x = nLeftWidth - aSubRect.size.width;
+ [rLeftColumn[i].pSubControl setFrame: aSubRect];
+ }
+ aCtrlRect.origin.x = nX;
+ [rLeftColumn[i].pControl setFrame: aCtrlRect];
+ }
+ }
+
+ // left align right column
+ for( size_t i = 0; i < rRightColumn.size(); i++ )
+ {
+ if( rRightColumn[i].pControl )
+ {
+ NSRect aCtrlRect = [rRightColumn[i].pControl frame];
+ long nX = nLeftWidth + 3;
+ if( rRightColumn[i].pSubControl )
+ {
+ NSRect aSubRect = [rRightColumn[i].pSubControl frame];
+ aSubRect.origin.x = nX + aSubRect.origin.x - aCtrlRect.origin.x;
+ [rRightColumn[i].pSubControl setFrame: aSubRect];
+ }
+ aCtrlRect.origin.x = nX;
+ [rRightColumn[i].pControl setFrame: aCtrlRect];
+ }
+ }
+
+ NSArray* pSubViews = [pNSView subviews];
+ unsigned int nViews = [pSubViews count];
+ NSRect aUnion = { { 0, 0 }, { 0, 0 } };
+
+ // get the combined frame of all subviews
+ for( unsigned int n = 0; n < nViews; n++ )
+ {
+ aUnion = NSUnionRect( aUnion, [[pSubViews objectAtIndex: n] frame] );
+ }
+
+ // move everything so it will fit
+ for( unsigned int n = 0; n < nViews; n++ )
+ {
+ NSView* pCurSubView = [pSubViews objectAtIndex: n];
+ NSRect aFrame = [pCurSubView frame];
+ aFrame.origin.x -= aUnion.origin.x - 5;
+ aFrame.origin.y -= aUnion.origin.y - 5;
+ [pCurSubView setFrame: aFrame];
+ }
+
+ // resize the view itself
+ aUnion.size.height += 10;
+ aUnion.size.width += 20;
+ [pNSView setFrameSize: aUnion.size];
+
+ if( aUnion.size.width > rMaxSize.width )
+ rMaxSize.width = aUnion.size.width;
+ if( aUnion.size.height > rMaxSize.height )
+ rMaxSize.height = aUnion.size.height;
+}
+
+static void adjustTabViews( NSTabView* pTabView, NSSize aTabSize )
+{
+ // loop over all contained tab pages
+ NSArray* pTabbedViews = [pTabView tabViewItems];
+ int nViews = [pTabbedViews count];
+ for( int i = 0; i < nViews; i++ )
+ {
+ NSTabViewItem* pItem = (NSTabViewItem*)[pTabbedViews objectAtIndex: i];
+ NSView* pNSView = [pItem view];
+ if( pNSView )
+ {
+ NSRect aRect = [pNSView frame];
+ double nDiff = aTabSize.height - aRect.size.height;
+ aRect.size = aTabSize;
+ [pNSView setFrame: aRect];
+
+ NSArray* pSubViews = [pNSView subviews];
+ unsigned int nSubViews = [pSubViews count];
+
+ // move everything up
+ for( unsigned int n = 0; n < nSubViews; n++ )
+ {
+ NSView* pCurSubView = [pSubViews objectAtIndex: n];
+ NSRect aFrame = [pCurSubView frame];
+ aFrame.origin.y += nDiff;
+ // give separators the correct width
+ // separators are currently the only NSBoxes we use
+ if( [pCurSubView isMemberOfClass: [NSBox class]] )
+ {
+ aFrame.size.width = aTabSize.width - aFrame.origin.x - 10;
+ }
+ [pCurSubView setFrame: aFrame];
+ }
+ }
+ }
+}
+
+static NSControl* createLabel( const rtl::OUString& i_rText )
+{
+ NSString* pText = CreateNSString( i_rText );
+ NSRect aTextRect = { { 0, 0 }, {20, 15} };
+ NSTextField* pTextView = [[NSTextField alloc] initWithFrame: aTextRect];
+ [pTextView setFont: [NSFont controlContentFontOfSize: 0]];
+ [pTextView setEditable: NO];
+ [pTextView setSelectable: NO];
+ [pTextView setDrawsBackground: NO];
+ [pTextView setBordered: NO];
+ [pTextView setStringValue: pText];
+ [pTextView sizeToFit];
+ [pText release];
+ return pTextView;
+}
+
+static sal_Int32 findBreak( const rtl::OUString& i_rText, sal_Int32 i_nPos )
+{
+ sal_Int32 nRet = i_rText.getLength();
+ Reference< i18n::XBreakIterator > xBI( vcl::unohelper::CreateBreakIterator() );
+ if( xBI.is() )
+ {
+ i18n::Boundary aBoundary = xBI->getWordBoundary( i_rText, i_nPos,
+ Application::GetSettings().GetLanguageTag().getLocale(),
+ i18n::WordType::ANYWORD_IGNOREWHITESPACES,
+ sal_True );
+ nRet = aBoundary.endPos;
+ }
+ return nRet;
+}
+
+static void linebreakCell( NSCell* pBtn, const rtl::OUString& i_rText )
+{
+ NSString* pText = CreateNSString( i_rText );
+ [pBtn setTitle: pText];
+ [pText release];
+ NSSize aSize = [pBtn cellSize];
+ if( aSize.width > 280 )
+ {
+ // need two lines
+ sal_Int32 nLen = i_rText.getLength();
+ sal_Int32 nIndex = nLen / 2;
+ nIndex = findBreak( i_rText, nIndex );
+ if( nIndex < nLen )
+ {
+ rtl::OUStringBuffer aBuf( i_rText );
+ aBuf[nIndex] = '\n';
+ pText = CreateNSString( aBuf.makeStringAndClear() );
+ [pBtn setTitle: pText];
+ [pText release];
+ }
+ }
+}
+
+static void addSubgroup( NSView* pCurParent, long& rCurY, const rtl::OUString& rText )
+{
+ NSControl* pTextView = createLabel( rText );
+ [pCurParent addSubview: [pTextView autorelease]];
+ NSRect aTextRect = [pTextView frame];
+ // move to nCurY
+ aTextRect.origin.y = rCurY - aTextRect.size.height;
+ [pTextView setFrame: aTextRect];
+
+ NSRect aSepRect = { { aTextRect.size.width + 1, aTextRect.origin.y }, { 100, 6 } };
+ NSBox* pBox = [[NSBox alloc] initWithFrame: aSepRect];
+ [pBox setBoxType: NSBoxSeparator];
+ [pCurParent addSubview: [pBox autorelease]];
+
+ // update nCurY
+ rCurY = aTextRect.origin.y - 5;
+}
+
+static void addBool( NSView* pCurParent, long& rCurX, long& rCurY, long nAttachOffset,
+ const rtl::OUString& rText, sal_Bool bEnabled,
+ const rtl::OUString& rProperty, sal_Bool bValue,
+ std::vector<ColumnItem >& rRightColumn,
+ ControllerProperties* pControllerProperties,
+ ControlTarget* pCtrlTarget
+ )
+{
+ NSRect aCheckRect = { { static_cast<CGFloat>(rCurX + nAttachOffset), 0 }, { 0, 15 } };
+ NSButton* pBtn = [[NSButton alloc] initWithFrame: aCheckRect];
+ [pBtn setButtonType: NSSwitchButton];
+ [pBtn setState: bValue ? NSOnState : NSOffState];
+ if( ! bEnabled )
+ [pBtn setEnabled: NO];
+ linebreakCell( [pBtn cell], rText );
+ [pBtn sizeToFit];
+
+ rRightColumn.push_back( ColumnItem( pBtn ) );
+
+ // connect target
+ [pBtn setTarget: pCtrlTarget];
+ [pBtn setAction: @selector(triggered:)];
+ int nTag = pControllerProperties->addNameTag( rProperty );
+ pControllerProperties->addObservedControl( pBtn );
+ [pBtn setTag: nTag];
+
+ aCheckRect = [pBtn frame];
+ // #i115837# add a murphy factor; it can apparently occasionally happen
+ // that sizeToFit does not a perfect job and that the button linebreaks again
+ // if - and only if - there is already a '\n' contained in the text and the width
+ // is minimally of
+ aCheckRect.size.width += 1;
+
+ // move to rCurY
+ aCheckRect.origin.y = rCurY - aCheckRect.size.height;
+ [pBtn setFrame: aCheckRect];
+
+ [pCurParent addSubview: [pBtn autorelease]];
+
+ // update rCurY
+ rCurY = aCheckRect.origin.y - 5;
+}
+
+static void addRadio( NSView* pCurParent, long& rCurX, long& rCurY, long nAttachOffset,
+ const rtl::OUString& rText,
+ const rtl::OUString& rProperty, Sequence< rtl::OUString > rChoices, sal_Int32 nSelectValue,
+ std::vector<ColumnItem >& rLeftColumn,
+ std::vector<ColumnItem >& rRightColumn,
+ ControllerProperties* pControllerProperties,
+ ControlTarget* pCtrlTarget
+ )
+{
+ sal_Int32 nOff = 0;
+ if( rText.getLength() )
+ {
+ // add a label
+ NSControl* pTextView = createLabel( rText );
+ NSRect aTextRect = [pTextView frame];
+ aTextRect.origin.x = rCurX + nAttachOffset;
+ [pCurParent addSubview: [pTextView autorelease]];
+
+ rLeftColumn.push_back( ColumnItem( pTextView ) );
+
+ // move to nCurY
+ aTextRect.origin.y = rCurY - aTextRect.size.height;
+ [pTextView setFrame: aTextRect];
+
+ // update nCurY
+ rCurY = aTextRect.origin.y - 5;
+
+ // indent the radio group relative to the text
+ // nOff = 20;
+ }
+
+ // setup radio matrix
+ NSButtonCell* pProto = [[NSButtonCell alloc] init];
+
+ NSRect aRadioRect = { { static_cast<CGFloat>(rCurX + nOff), 0 }, { static_cast<CGFloat>(280 - rCurX), static_cast<CGFloat>(5*rChoices.getLength()) } };
+ [pProto setTitle: @"RadioButtonGroup"];
+ [pProto setButtonType: NSRadioButton];
+ NSMatrix* pMatrix = [[NSMatrix alloc] initWithFrame: aRadioRect
+ mode: NSRadioModeMatrix
+ prototype: (NSCell*)pProto
+ numberOfRows: rChoices.getLength()
+ numberOfColumns: 1];
+ // set individual titles
+ NSArray* pCells = [pMatrix cells];
+ for( sal_Int32 m = 0; m < rChoices.getLength(); m++ )
+ {
+ NSCell* pCell = [pCells objectAtIndex: m];
+ filterAccelerator( rChoices[m] );
+ linebreakCell( pCell, rChoices[m] );
+ // connect target and action
+ [pCell setTarget: pCtrlTarget];
+ [pCell setAction: @selector(triggered:)];
+ int nTag = pControllerProperties->addNameAndValueTag( rProperty, m );
+ pControllerProperties->addObservedControl( pCell );
+ [pCell setTag: nTag];
+ // set current selection
+ if( nSelectValue == m )
+ [pMatrix selectCellAtRow: m column: 0];
+ }
+ [pMatrix sizeToFit];
+ aRadioRect = [pMatrix frame];
+
+ // move it down, so it comes to the correct position
+ aRadioRect.origin.y = rCurY - aRadioRect.size.height;
+ [pMatrix setFrame: aRadioRect];
+ [pCurParent addSubview: [pMatrix autorelease]];
+
+ rRightColumn.push_back( ColumnItem( pMatrix ) );
+
+ // update nCurY
+ rCurY = aRadioRect.origin.y - 5;
+
+ [pProto release];
+}
+
+static void addList( NSView* pCurParent, long& rCurX, long& rCurY, long /*nAttachOffset*/,
+ const rtl::OUString& rText,
+ const rtl::OUString& rProperty, const Sequence< rtl::OUString > rChoices, sal_Int32 nSelectValue,
+ std::vector<ColumnItem >& rLeftColumn,
+ std::vector<ColumnItem >& rRightColumn,
+ ControllerProperties* pControllerProperties,
+ ControlTarget* pCtrlTarget
+ )
+{
+ // don't indent attached lists, looks bad in the existing cases
+ NSControl* pTextView = createLabel( rText );
+ [pCurParent addSubview: [pTextView autorelease]];
+ rLeftColumn.push_back( ColumnItem( pTextView ) );
+ NSRect aTextRect = [pTextView frame];
+ aTextRect.origin.x = rCurX /* + nAttachOffset*/;
+
+ // don't indent attached lists, looks bad in the existing cases
+ NSRect aBtnRect = { { rCurX /*+ nAttachOffset*/ + aTextRect.size.width, 0 }, { 0, 15 } };
+ NSPopUpButton* pBtn = [[NSPopUpButton alloc] initWithFrame: aBtnRect pullsDown: NO];
+
+ // iterate options
+ for( sal_Int32 m = 0; m < rChoices.getLength(); m++ )
+ {
+ NSString* pItemText = CreateNSString( rChoices[m] );
+ [pBtn addItemWithTitle: pItemText];
+ NSMenuItem* pItem = [pBtn itemWithTitle: pItemText];
+ int nTag = pControllerProperties->addNameAndValueTag( rProperty, m );
+ [pItem setTag: nTag];
+ [pItemText release];
+ }
+
+ [pBtn selectItemAtIndex: nSelectValue];
+
+ // add the button to observed controls for enabled state changes
+ // also add a tag just for this purpose
+ pControllerProperties->addObservedControl( pBtn );
+ [pBtn setTag: pControllerProperties->addNameTag( rProperty )];
+
+ [pBtn sizeToFit];
+ [pCurParent addSubview: [pBtn autorelease]];
+
+ rRightColumn.push_back( ColumnItem( pBtn ) );
+
+ // connect target and action
+ [pBtn setTarget: pCtrlTarget];
+ [pBtn setAction: @selector(triggered:)];
+
+ // move to nCurY
+ aBtnRect = [pBtn frame];
+ aBtnRect.origin.y = rCurY - aBtnRect.size.height;
+ [pBtn setFrame: aBtnRect];
+
+ // align label
+ aTextRect.origin.y = aBtnRect.origin.y + (aBtnRect.size.height - aTextRect.size.height)/2;
+ [pTextView setFrame: aTextRect];
+
+ // update rCurY
+ rCurY = aBtnRect.origin.y - 5;
+}
+
+static void addEdit( NSView* pCurParent, long& rCurX, long& rCurY, long nAttachOffset,
+ const rtl::OUString rCtrlType,
+ const rtl::OUString& rText,
+ const rtl::OUString& rProperty, const PropertyValue* pValue,
+ sal_Int64 nMinValue, sal_Int64 nMaxValue,
+ std::vector<ColumnItem >& rLeftColumn,
+ std::vector<ColumnItem >& rRightColumn,
+ ControllerProperties* pControllerProperties,
+ ControlTarget* pCtrlTarget
+ )
+{
+ sal_Int32 nOff = 0;
+ if( rText.getLength() )
+ {
+ // add a label
+ NSControl* pTextView = createLabel( rText );
+ [pCurParent addSubview: [pTextView autorelease]];
+
+ rLeftColumn.push_back( ColumnItem( pTextView ) );
+
+ // move to nCurY
+ NSRect aTextRect = [pTextView frame];
+ aTextRect.origin.x = rCurX + nAttachOffset;
+ aTextRect.origin.y = rCurY - aTextRect.size.height;
+ [pTextView setFrame: aTextRect];
+
+ // update nCurY
+ rCurY = aTextRect.origin.y - 5;
+
+ // and set the offset for the real edit field
+ nOff = aTextRect.size.width + 5;
+ }
+
+ NSRect aFieldRect = { { static_cast<CGFloat>(rCurX + nOff + nAttachOffset), 0 }, { 100, 25 } };
+ NSTextField* pFieldView = [[NSTextField alloc] initWithFrame: aFieldRect];
+ [pFieldView setEditable: YES];
+ [pFieldView setSelectable: YES];
+ [pFieldView setDrawsBackground: YES];
+ [pFieldView sizeToFit]; // FIXME: this does nothing
+ [pCurParent addSubview: [pFieldView autorelease]];
+
+ rRightColumn.push_back( ColumnItem( pFieldView ) );
+
+ // add the field to observed controls for enabled state changes
+ // also add a tag just for this purpose
+ pControllerProperties->addObservedControl( pFieldView );
+ int nTag = pControllerProperties->addNameTag( rProperty );
+ [pFieldView setTag: nTag];
+ // pControllerProperties->addNamedView( pFieldView, aPropertyName );
+
+ // move to nCurY
+ aFieldRect.origin.y = rCurY - aFieldRect.size.height;
+ [pFieldView setFrame: aFieldRect];
+
+ if( rCtrlType.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Range" ) ) )
+ {
+ // add a stepper control
+ NSRect aStepFrame = { { aFieldRect.origin.x + aFieldRect.size.width + 5,
+ aFieldRect.origin.y },
+ { 15, aFieldRect.size.height } };
+ NSStepper* pStep = [[NSStepper alloc] initWithFrame: aStepFrame];
+ [pStep setIncrement: 1];
+ [pStep setValueWraps: NO];
+ [pStep setTag: nTag];
+ [pCurParent addSubview: [pStep autorelease]];
+
+ rRightColumn.back().pSubControl = pStep;
+
+ pControllerProperties->addObservedControl( pStep );
+ [pStep setTarget: pCtrlTarget];
+ [pStep setAction: @selector(triggered:)];
+
+ // constrain the text field to decimal numbers
+ NSNumberFormatter* pFormatter = [[NSNumberFormatter alloc] init];
+ [pFormatter setFormatterBehavior: NSNumberFormatterBehavior10_4];
+ [pFormatter setNumberStyle: NSNumberFormatterDecimalStyle];
+ [pFormatter setAllowsFloats: NO];
+ [pFormatter setMaximumFractionDigits: 0];
+ if( nMinValue != nMaxValue )
+ {
+ [pFormatter setMinimum: [[NSNumber numberWithInt: nMinValue] autorelease]];
+ [pStep setMinValue: nMinValue];
+ [pFormatter setMaximum: [[NSNumber numberWithInt: nMaxValue] autorelease]];
+ [pStep setMaxValue: nMaxValue];
+ }
+ [pFieldView setFormatter: pFormatter];
+
+ sal_Int64 nSelectVal = 0;
+ if( pValue && pValue->Value.hasValue() )
+ pValue->Value >>= nSelectVal;
+
+ [pFieldView setIntValue: nSelectVal];
+ [pStep setIntValue: nSelectVal];
+
+ pControllerProperties->addViewPair( pFieldView, pStep );
+ // connect target and action
+ [pFieldView setTarget: pCtrlTarget];
+ [pFieldView setAction: @selector(triggeredNumeric:)];
+ [pStep setTarget: pCtrlTarget];
+ [pStep setAction: @selector(triggeredNumeric:)];
+ }
+ else
+ {
+ // connect target and action
+ [pFieldView setTarget: pCtrlTarget];
+ [pFieldView setAction: @selector(triggered:)];
+
+ if( pValue && pValue->Value.hasValue() )
+ {
+ rtl::OUString aValue;
+ pValue->Value >>= aValue;
+ if( aValue.getLength() )
+ {
+ NSString* pText = CreateNSString( aValue );
+ [pFieldView setStringValue: pText];
+ [pText release];
+ }
+ }
+ }
+
+ // update nCurY
+ rCurY = aFieldRect.origin.y - 5;
+}
+
+// In 10.5 and later:
+// 'setAccessoryView:' is deprecated
+
+// Make deprecation warnings just warnings in a -Werror compilation.
+
+#if HAVE_GCC_PRAGMA_DIAGNOSTIC_MODIFY
+// #pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-Wdeprecated-declarations"
+#endif
+
+@implementation AquaPrintAccessoryView
++(NSObject*)setupPrinterPanel: (NSPrintOperation*)pOp withController: (vcl::PrinterController*)pController withState: (PrintAccessoryViewState*)pState
+{
+ const Sequence< PropertyValue >& rOptions( pController->getUIOptions() );
+ if( rOptions.getLength() == 0 )
+ return nil;
+
+ NSView* pCurParent = 0;
+ long nCurY = 0;
+ long nCurX = 0;
+ NSRect aViewFrame = { { 0, 0 }, {600, 400 } };
+ NSRect aTabViewFrame = { { 190, 0 }, {410, 400 } };
+ NSSize aMaxTabSize = { 0, 0 };
+ NSView* pAccessoryView = [[NSView alloc] initWithFrame: aViewFrame];
+ NSTabView* pTabView = [[NSTabView alloc] initWithFrame: aTabViewFrame];
+ [pAccessoryView addSubview: [pTabView autorelease]];
+
+ sal_Bool bIgnoreSubgroup = sal_False;
+
+ ControllerProperties* pControllerProperties = new ControllerProperties( pController, pOp, pAccessoryView, pTabView, pState );
+ ControlTarget* pCtrlTarget = [[ControlTarget alloc] initWithControllerMap: pControllerProperties];
+
+ std::vector< ColumnItem > aLeftColumn, aRightColumn;
+
+ // ugly:
+ // prepend a "selection" checkbox if the properties have such a selection in PrintContent
+ bool bAddSelectionCheckBox = false, bSelectionBoxEnabled = false, bSelectionBoxChecked = false;
+ for( int i = 0; i < rOptions.getLength(); i++ )
+ {
+ Sequence< beans::PropertyValue > aOptProp;
+ rOptions[i].Value >>= aOptProp;
+
+ rtl::OUString aCtrlType;
+ rtl::OUString aPropertyName;
+ Sequence< rtl::OUString > aChoices;
+ Sequence< sal_Bool > aChoicesDisabled;
+ sal_Int32 aSelectionChecked = 0;
+ for( int n = 0; n < aOptProp.getLength(); n++ )
+ {
+ const beans::PropertyValue& rEntry( aOptProp[ n ] );
+ if( rEntry.Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("ControlType")) )
+ {
+ rEntry.Value >>= aCtrlType;
+ }
+ else if( rEntry.Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Choices")) )
+ {
+ rEntry.Value >>= aChoices;
+ }
+ else if( rEntry.Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("ChoicesDisabled")) )
+ {
+ rEntry.Value >>= aChoicesDisabled;
+ }
+ else if( rEntry.Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Property")) )
+ {
+ PropertyValue aVal;
+ rEntry.Value >>= aVal;
+ aPropertyName = aVal.Name;
+ if( aPropertyName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("PrintContent")) )
+ aVal.Value >>= aSelectionChecked;
+ }
+ }
+ if( aCtrlType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Radio")) &&
+ aPropertyName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("PrintContent")) &&
+ aChoices.getLength() > 2 )
+ {
+ bAddSelectionCheckBox = true;
+ bSelectionBoxEnabled = aChoicesDisabled.getLength() < 2 || ! aChoicesDisabled[2];
+ bSelectionBoxChecked = (aSelectionChecked==2);
+ break;
+ }
+ }
+
+ for( int i = 0; i < rOptions.getLength(); i++ )
+ {
+ Sequence< beans::PropertyValue > aOptProp;
+ rOptions[i].Value >>= aOptProp;
+
+ // extract ui element
+ bool bEnabled = true;
+ rtl::OUString aCtrlType;
+ rtl::OUString aText;
+ rtl::OUString aPropertyName;
+ rtl::OUString aGroupHint;
+ Sequence< rtl::OUString > aChoices;
+ sal_Int64 nMinValue = 0, nMaxValue = 0;
+ long nAttachOffset = 0;
+ sal_Bool bIgnore = sal_False;
+
+ for( int n = 0; n < aOptProp.getLength(); n++ )
+ {
+ const beans::PropertyValue& rEntry( aOptProp[ n ] );
+ if( rEntry.Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Text")) )
+ {
+ rEntry.Value >>= aText;
+ filterAccelerator( aText );
+ }
+ else if( rEntry.Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("ControlType")) )
+ {
+ rEntry.Value >>= aCtrlType;
+ }
+ else if( rEntry.Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Choices")) )
+ {
+ rEntry.Value >>= aChoices;
+ }
+ else if( rEntry.Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Property")) )
+ {
+ PropertyValue aVal;
+ rEntry.Value >>= aVal;
+ aPropertyName = aVal.Name;
+ }
+ else if( rEntry.Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Enabled")) )
+ {
+ sal_Bool bValue = sal_True;
+ rEntry.Value >>= bValue;
+ bEnabled = bValue;
+ }
+ else if( rEntry.Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("MinValue")) )
+ {
+ rEntry.Value >>= nMinValue;
+ }
+ else if( rEntry.Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("MaxValue")) )
+ {
+ rEntry.Value >>= nMaxValue;
+ }
+ else if( rEntry.Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("AttachToDependency")) )
+ {
+ nAttachOffset = 20;
+ }
+ else if( rEntry.Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("InternalUIOnly")) )
+ {
+ rEntry.Value >>= bIgnore;
+ }
+ else if( rEntry.Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("GroupingHint")) )
+ {
+ rEntry.Value >>= aGroupHint;
+ }
+ }
+
+ if( aCtrlType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Group")) ||
+ aCtrlType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Subgroup")) ||
+ aCtrlType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Radio")) ||
+ aCtrlType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("List")) ||
+ aCtrlType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Edit")) ||
+ aCtrlType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Range")) ||
+ aCtrlType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Bool")) )
+ {
+ // since our build target is MacOSX 10.4 we can have only one accessory view
+ // so we have a single accessory view that is tabbed for grouping
+ if( aCtrlType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Group"))
+ || ! pCurParent
+ || ( aCtrlType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Subgroup")) && nCurY < -250 && ! bIgnore )
+ )
+ {
+ rtl::OUString aGroupTitle( aText );
+ if( aCtrlType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Subgroup")) )
+ aGroupTitle = pControllerProperties->getMoreString();
+ // set size of current parent
+ if( pCurParent )
+ adjustViewAndChildren( pCurParent, aMaxTabSize, aLeftColumn, aRightColumn );
+
+ // new tab item
+ if( ! aText.getLength() )
+ aText = OUString( "OOo" );
+ NSString* pLabel = CreateNSString( aGroupTitle );
+ NSTabViewItem* pItem = [[NSTabViewItem alloc] initWithIdentifier: pLabel ];
+ [pItem setLabel: pLabel];
+ [pTabView addTabViewItem: pItem];
+ pCurParent = [[NSView alloc] initWithFrame: aTabViewFrame];
+ [pItem setView: pCurParent];
+ [pLabel release];
+
+ // reset indent
+ nCurX = 20;
+ // reset Y
+ nCurY = 0;
+ // clear columns
+ aLeftColumn.clear();
+ aRightColumn.clear();
+
+ if( bAddSelectionCheckBox )
+ {
+ addBool( pCurParent, nCurX, nCurY, 0,
+ pControllerProperties->getPrintSelectionString(), bSelectionBoxEnabled,
+ OUString( "PrintContent" ), bSelectionBoxChecked,
+ aRightColumn, pControllerProperties, pCtrlTarget );
+ bAddSelectionCheckBox = false;
+ }
+ }
+
+ if( aCtrlType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Subgroup")) && pCurParent )
+ {
+ bIgnoreSubgroup = bIgnore;
+ if( bIgnore )
+ continue;
+
+ addSubgroup( pCurParent, nCurY, aText );
+ }
+ else if( bIgnoreSubgroup || bIgnore )
+ {
+ continue;
+ }
+ else if( aCtrlType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Bool")) && pCurParent )
+ {
+ sal_Bool bVal = sal_False;
+ PropertyValue* pVal = pController->getValue( aPropertyName );
+ if( pVal )
+ pVal->Value >>= bVal;
+ addBool( pCurParent, nCurX, nCurY, nAttachOffset,
+ aText, true, aPropertyName, bVal,
+ aRightColumn, pControllerProperties, pCtrlTarget );
+ }
+ else if( aCtrlType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Radio")) && pCurParent )
+ {
+ // get currently selected value
+ sal_Int32 nSelectVal = 0;
+ PropertyValue* pVal = pController->getValue( aPropertyName );
+ if( pVal && pVal->Value.hasValue() )
+ pVal->Value >>= nSelectVal;
+
+ addRadio( pCurParent, nCurX, nCurY, nAttachOffset,
+ aText, aPropertyName, aChoices, nSelectVal,
+ aLeftColumn, aRightColumn,
+ pControllerProperties, pCtrlTarget );
+ }
+ else if( aCtrlType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("List")) && pCurParent )
+ {
+ PropertyValue* pVal = pController->getValue( aPropertyName );
+ sal_Int32 aSelectVal = 0;
+ if( pVal && pVal->Value.hasValue() )
+ pVal->Value >>= aSelectVal;
+
+ addList( pCurParent, nCurX, nCurY, nAttachOffset,
+ aText, aPropertyName, aChoices, aSelectVal,
+ aLeftColumn, aRightColumn,
+ pControllerProperties, pCtrlTarget );
+ }
+ else if( (aCtrlType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Edit")) || aCtrlType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Range"))) && pCurParent )
+ {
+ // current value
+ PropertyValue* pVal = pController->getValue( aPropertyName );
+ addEdit( pCurParent, nCurX, nCurY, nAttachOffset,
+ aCtrlType, aText, aPropertyName, pVal,
+ nMinValue, nMaxValue,
+ aLeftColumn, aRightColumn,
+ pControllerProperties, pCtrlTarget );
+ }
+ }
+ else
+ {
+ SAL_INFO( "vcl.osx.print", "Unsupported UI option \"" << aCtrlType << "\"");
+ }
+ }
+
+ pControllerProperties->updateEnableState();
+ adjustViewAndChildren( pCurParent, aMaxTabSize, aLeftColumn, aRightColumn );
+
+ // leave some space for the preview
+ if( aMaxTabSize.height < 200 )
+ aMaxTabSize.height = 200;
+
+ // now reposition everything again so it is upper bound
+ adjustTabViews( pTabView, aMaxTabSize );
+
+ // find the minimum needed tab size
+ NSSize aTabCtrlSize = [pTabView minimumSize];
+ aTabCtrlSize.height += aMaxTabSize.height + 10;
+ if( aTabCtrlSize.width < aMaxTabSize.width + 10 )
+ aTabCtrlSize.width = aMaxTabSize.width + 10;
+ [pTabView setFrameSize: aTabCtrlSize];
+ aViewFrame.size.width = aTabCtrlSize.width + aTabViewFrame.origin.x;
+ aViewFrame.size.height = aTabCtrlSize.height + aTabViewFrame.origin.y;
+ [pAccessoryView setFrameSize: aViewFrame.size];
+
+ pControllerProperties->setupPreview( pCtrlTarget );
+
+ // set the accessory view
+ [pOp setAccessoryView: [pAccessoryView autorelease]];
+
+ // set the current selecte tab item
+ if( pState->nLastPage >= 0 && pState->nLastPage < [pTabView numberOfTabViewItems] )
+ [pTabView selectTabViewItemAtIndex: pState->nLastPage];
+
+ return pCtrlTarget;
+}
+
+// #pragma GCC diagnostic pop
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/printview.mm b/vcl/osx/printview.mm
new file mode 100644
index 000000000000..d2f4ac006d71
--- /dev/null
+++ b/vcl/osx/printview.mm
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "vcl/print.hxx"
+
+#include "osx/printview.h"
+#include "osx/salprn.h"
+
+@implementation AquaPrintView
+-(id)initWithController: (vcl::PrinterController*)pController withInfoPrinter: (AquaSalInfoPrinter*)pInfoPrinter
+{
+ NSRect aRect = { { 0, 0 }, [pInfoPrinter->getPrintInfo() paperSize] };
+ if( (self = [super initWithFrame: aRect]) != nil )
+ {
+ mpController = pController;
+ mpInfoPrinter = pInfoPrinter;
+ }
+ return self;
+}
+
+-(BOOL)knowsPageRange: (NSRangePointer)range
+{
+ range->location = 1;
+ range->length = mpInfoPrinter->getCurPageRangeCount();
+ return YES;
+}
+
+-(NSRect)rectForPage: (int)page
+{
+ NSSize aPaperSize = [mpInfoPrinter->getPrintInfo() paperSize];
+ int nWidth = (int)aPaperSize.width;
+ // #i101108# sanity check
+ if( nWidth < 1 )
+ nWidth = 1;
+ NSRect aRect = { { static_cast<CGFloat>(page % nWidth), static_cast<CGFloat>(page / nWidth) }, aPaperSize };
+ return aRect;
+}
+
+-(NSPoint)locationOfPrintRect: (NSRect)aRect
+{
+ (void)aRect;
+ NSPoint aPoint = { 0, 0 };
+ return aPoint;
+}
+
+-(void)drawRect: (NSRect)rect
+{
+ mpInfoPrinter->setStartPageOffset( static_cast<int>(rect.origin.x), static_cast<int>(rect.origin.y) );
+ NSSize aPaperSize = [mpInfoPrinter->getPrintInfo() paperSize];
+ int nPage = (int)(aPaperSize.width * rect.origin.y + rect.origin.x);
+
+ // page count is 1 based
+ if( nPage - 1 < (mpInfoPrinter->getCurPageRangeStart() + mpInfoPrinter->getCurPageRangeCount() ) )
+ mpController->printFilteredPage( nPage-1 );
+}
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/res/MainMenu.nib/classes.nib b/vcl/osx/res/MainMenu.nib/classes.nib
new file mode 100644
index 000000000000..b9b4b09f6b0d
--- /dev/null
+++ b/vcl/osx/res/MainMenu.nib/classes.nib
@@ -0,0 +1,4 @@
+{
+ IBClasses = ({CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; });
+ IBVersion = 1;
+} \ No newline at end of file
diff --git a/vcl/osx/res/MainMenu.nib/info.nib b/vcl/osx/res/MainMenu.nib/info.nib
new file mode 100644
index 000000000000..856429aee5bd
--- /dev/null
+++ b/vcl/osx/res/MainMenu.nib/info.nib
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IBDocumentLocation</key>
+ <string>135 107 356 240 0 0 1680 1028 </string>
+ <key>IBEditorPositions</key>
+ <dict>
+ <key>29</key>
+ <string>132 352 141 44 0 0 1680 1028 </string>
+ </dict>
+ <key>IBFramework Version</key>
+ <string>446.1</string>
+ <key>IBOpenObjects</key>
+ <array>
+ <integer>29</integer>
+ </array>
+ <key>IBSystem Version</key>
+ <string>8R2218</string>
+</dict>
+</plist>
diff --git a/vcl/osx/res/MainMenu.nib/keyedobjects.nib b/vcl/osx/res/MainMenu.nib/keyedobjects.nib
new file mode 100644
index 000000000000..d39d10119c0c
--- /dev/null
+++ b/vcl/osx/res/MainMenu.nib/keyedobjects.nib
Binary files differ
diff --git a/vcl/osx/res/cursors/airbrush.png b/vcl/osx/res/cursors/airbrush.png
new file mode 100644
index 000000000000..7ec780c4f9f9
--- /dev/null
+++ b/vcl/osx/res/cursors/airbrush.png
Binary files differ
diff --git a/vcl/osx/res/cursors/ase.png b/vcl/osx/res/cursors/ase.png
new file mode 100644
index 000000000000..a3a30e0bcdce
--- /dev/null
+++ b/vcl/osx/res/cursors/ase.png
Binary files differ
diff --git a/vcl/osx/res/cursors/asn.png b/vcl/osx/res/cursors/asn.png
new file mode 100644
index 000000000000..7a140b1ec926
--- /dev/null
+++ b/vcl/osx/res/cursors/asn.png
Binary files differ
diff --git a/vcl/osx/res/cursors/asne.png b/vcl/osx/res/cursors/asne.png
new file mode 100644
index 000000000000..311506aeb349
--- /dev/null
+++ b/vcl/osx/res/cursors/asne.png
Binary files differ
diff --git a/vcl/osx/res/cursors/asns.png b/vcl/osx/res/cursors/asns.png
new file mode 100644
index 000000000000..1c8950eb28bc
--- /dev/null
+++ b/vcl/osx/res/cursors/asns.png
Binary files differ
diff --git a/vcl/osx/res/cursors/asnswe.png b/vcl/osx/res/cursors/asnswe.png
new file mode 100644
index 000000000000..aae5246fbbc0
--- /dev/null
+++ b/vcl/osx/res/cursors/asnswe.png
Binary files differ
diff --git a/vcl/osx/res/cursors/asnw.png b/vcl/osx/res/cursors/asnw.png
new file mode 100644
index 000000000000..9fd0036df077
--- /dev/null
+++ b/vcl/osx/res/cursors/asnw.png
Binary files differ
diff --git a/vcl/osx/res/cursors/ass.png b/vcl/osx/res/cursors/ass.png
new file mode 100644
index 000000000000..bee09e736ad1
--- /dev/null
+++ b/vcl/osx/res/cursors/ass.png
Binary files differ
diff --git a/vcl/osx/res/cursors/asse.png b/vcl/osx/res/cursors/asse.png
new file mode 100644
index 000000000000..d7883211d44f
--- /dev/null
+++ b/vcl/osx/res/cursors/asse.png
Binary files differ
diff --git a/vcl/osx/res/cursors/assw.png b/vcl/osx/res/cursors/assw.png
new file mode 100644
index 000000000000..0b0a496a52ec
--- /dev/null
+++ b/vcl/osx/res/cursors/assw.png
Binary files differ
diff --git a/vcl/osx/res/cursors/asw.png b/vcl/osx/res/cursors/asw.png
new file mode 100644
index 000000000000..5a4b9519e075
--- /dev/null
+++ b/vcl/osx/res/cursors/asw.png
Binary files differ
diff --git a/vcl/osx/res/cursors/aswe.png b/vcl/osx/res/cursors/aswe.png
new file mode 100644
index 000000000000..b9c5afaac043
--- /dev/null
+++ b/vcl/osx/res/cursors/aswe.png
Binary files differ
diff --git a/vcl/osx/res/cursors/chain.png b/vcl/osx/res/cursors/chain.png
new file mode 100644
index 000000000000..dbf069924d73
--- /dev/null
+++ b/vcl/osx/res/cursors/chain.png
Binary files differ
diff --git a/vcl/osx/res/cursors/chainnot.png b/vcl/osx/res/cursors/chainnot.png
new file mode 100644
index 000000000000..547703edf12c
--- /dev/null
+++ b/vcl/osx/res/cursors/chainnot.png
Binary files differ
diff --git a/vcl/osx/res/cursors/chart.png b/vcl/osx/res/cursors/chart.png
new file mode 100644
index 000000000000..de5514006e1f
--- /dev/null
+++ b/vcl/osx/res/cursors/chart.png
Binary files differ
diff --git a/vcl/osx/res/cursors/copydata.png b/vcl/osx/res/cursors/copydata.png
new file mode 100644
index 000000000000..b6202fd9144f
--- /dev/null
+++ b/vcl/osx/res/cursors/copydata.png
Binary files differ
diff --git a/vcl/osx/res/cursors/copydlnk.png b/vcl/osx/res/cursors/copydlnk.png
new file mode 100644
index 000000000000..fab24c9f8f7c
--- /dev/null
+++ b/vcl/osx/res/cursors/copydlnk.png
Binary files differ
diff --git a/vcl/osx/res/cursors/copyf.png b/vcl/osx/res/cursors/copyf.png
new file mode 100644
index 000000000000..70546d0c0c22
--- /dev/null
+++ b/vcl/osx/res/cursors/copyf.png
Binary files differ
diff --git a/vcl/osx/res/cursors/copyf2.png b/vcl/osx/res/cursors/copyf2.png
new file mode 100644
index 000000000000..b6f76051f10f
--- /dev/null
+++ b/vcl/osx/res/cursors/copyf2.png
Binary files differ
diff --git a/vcl/osx/res/cursors/copyflnk.png b/vcl/osx/res/cursors/copyflnk.png
new file mode 100644
index 000000000000..23561e484e36
--- /dev/null
+++ b/vcl/osx/res/cursors/copyflnk.png
Binary files differ
diff --git a/vcl/osx/res/cursors/crook.png b/vcl/osx/res/cursors/crook.png
new file mode 100644
index 000000000000..4378f8df8351
--- /dev/null
+++ b/vcl/osx/res/cursors/crook.png
Binary files differ
diff --git a/vcl/osx/res/cursors/crop.png b/vcl/osx/res/cursors/crop.png
new file mode 100644
index 000000000000..92a778ada31a
--- /dev/null
+++ b/vcl/osx/res/cursors/crop.png
Binary files differ
diff --git a/vcl/osx/res/cursors/darc.png b/vcl/osx/res/cursors/darc.png
new file mode 100644
index 000000000000..9772a1c6b85a
--- /dev/null
+++ b/vcl/osx/res/cursors/darc.png
Binary files differ
diff --git a/vcl/osx/res/cursors/dbezier.png b/vcl/osx/res/cursors/dbezier.png
new file mode 100644
index 000000000000..988498137e9a
--- /dev/null
+++ b/vcl/osx/res/cursors/dbezier.png
Binary files differ
diff --git a/vcl/osx/res/cursors/dcapt.png b/vcl/osx/res/cursors/dcapt.png
new file mode 100644
index 000000000000..d1ef82818735
--- /dev/null
+++ b/vcl/osx/res/cursors/dcapt.png
Binary files differ
diff --git a/vcl/osx/res/cursors/dcirccut.png b/vcl/osx/res/cursors/dcirccut.png
new file mode 100644
index 000000000000..cb4ed0e85ecd
--- /dev/null
+++ b/vcl/osx/res/cursors/dcirccut.png
Binary files differ
diff --git a/vcl/osx/res/cursors/dconnect.png b/vcl/osx/res/cursors/dconnect.png
new file mode 100644
index 000000000000..e4a43bdbe021
--- /dev/null
+++ b/vcl/osx/res/cursors/dconnect.png
Binary files differ
diff --git a/vcl/osx/res/cursors/dellipse.png b/vcl/osx/res/cursors/dellipse.png
new file mode 100644
index 000000000000..319c4574c7c1
--- /dev/null
+++ b/vcl/osx/res/cursors/dellipse.png
Binary files differ
diff --git a/vcl/osx/res/cursors/detectiv.png b/vcl/osx/res/cursors/detectiv.png
new file mode 100644
index 000000000000..abe93f263d4d
--- /dev/null
+++ b/vcl/osx/res/cursors/detectiv.png
Binary files differ
diff --git a/vcl/osx/res/cursors/dfree.png b/vcl/osx/res/cursors/dfree.png
new file mode 100644
index 000000000000..2de92942adde
--- /dev/null
+++ b/vcl/osx/res/cursors/dfree.png
Binary files differ
diff --git a/vcl/osx/res/cursors/dline.png b/vcl/osx/res/cursors/dline.png
new file mode 100644
index 000000000000..6afb670ef8a8
--- /dev/null
+++ b/vcl/osx/res/cursors/dline.png
Binary files differ
diff --git a/vcl/osx/res/cursors/dpie.png b/vcl/osx/res/cursors/dpie.png
new file mode 100644
index 000000000000..44a9474846b9
--- /dev/null
+++ b/vcl/osx/res/cursors/dpie.png
Binary files differ
diff --git a/vcl/osx/res/cursors/dpolygon.png b/vcl/osx/res/cursors/dpolygon.png
new file mode 100644
index 000000000000..847e6ad9bea5
--- /dev/null
+++ b/vcl/osx/res/cursors/dpolygon.png
Binary files differ
diff --git a/vcl/osx/res/cursors/drect.png b/vcl/osx/res/cursors/drect.png
new file mode 100644
index 000000000000..ff3dcbba07b4
--- /dev/null
+++ b/vcl/osx/res/cursors/drect.png
Binary files differ
diff --git a/vcl/osx/res/cursors/dtext.png b/vcl/osx/res/cursors/dtext.png
new file mode 100644
index 000000000000..ee375f0e47a0
--- /dev/null
+++ b/vcl/osx/res/cursors/dtext.png
Binary files differ
diff --git a/vcl/osx/res/cursors/fill.png b/vcl/osx/res/cursors/fill.png
new file mode 100644
index 000000000000..220641b9beb4
--- /dev/null
+++ b/vcl/osx/res/cursors/fill.png
Binary files differ
diff --git a/vcl/osx/res/cursors/help.png b/vcl/osx/res/cursors/help.png
new file mode 100644
index 000000000000..e29c19eccd22
--- /dev/null
+++ b/vcl/osx/res/cursors/help.png
Binary files differ
diff --git a/vcl/osx/res/cursors/hourglass.png b/vcl/osx/res/cursors/hourglass.png
new file mode 100644
index 000000000000..07bb5af73e6e
--- /dev/null
+++ b/vcl/osx/res/cursors/hourglass.png
Binary files differ
diff --git a/vcl/osx/res/cursors/hshear.png b/vcl/osx/res/cursors/hshear.png
new file mode 100644
index 000000000000..b45beded2d93
--- /dev/null
+++ b/vcl/osx/res/cursors/hshear.png
Binary files differ
diff --git a/vcl/osx/res/cursors/linkdata.png b/vcl/osx/res/cursors/linkdata.png
new file mode 100644
index 000000000000..6432db0155b6
--- /dev/null
+++ b/vcl/osx/res/cursors/linkdata.png
Binary files differ
diff --git a/vcl/osx/res/cursors/linkf.png b/vcl/osx/res/cursors/linkf.png
new file mode 100644
index 000000000000..e17107fec9ff
--- /dev/null
+++ b/vcl/osx/res/cursors/linkf.png
Binary files differ
diff --git a/vcl/osx/res/cursors/magnify.png b/vcl/osx/res/cursors/magnify.png
new file mode 100644
index 000000000000..4e73146b91e4
--- /dev/null
+++ b/vcl/osx/res/cursors/magnify.png
Binary files differ
diff --git a/vcl/osx/res/cursors/mirror.png b/vcl/osx/res/cursors/mirror.png
new file mode 100644
index 000000000000..8fac93f0b6df
--- /dev/null
+++ b/vcl/osx/res/cursors/mirror.png
Binary files differ
diff --git a/vcl/osx/res/cursors/movebw.png b/vcl/osx/res/cursors/movebw.png
new file mode 100644
index 000000000000..63bf76ad3942
--- /dev/null
+++ b/vcl/osx/res/cursors/movebw.png
Binary files differ
diff --git a/vcl/osx/res/cursors/movedata.png b/vcl/osx/res/cursors/movedata.png
new file mode 100644
index 000000000000..60ece8a53e59
--- /dev/null
+++ b/vcl/osx/res/cursors/movedata.png
Binary files differ
diff --git a/vcl/osx/res/cursors/movedlnk.png b/vcl/osx/res/cursors/movedlnk.png
new file mode 100644
index 000000000000..6951cd718d97
--- /dev/null
+++ b/vcl/osx/res/cursors/movedlnk.png
Binary files differ
diff --git a/vcl/osx/res/cursors/movef.png b/vcl/osx/res/cursors/movef.png
new file mode 100644
index 000000000000..97a01c88fa4d
--- /dev/null
+++ b/vcl/osx/res/cursors/movef.png
Binary files differ
diff --git a/vcl/osx/res/cursors/movef2.png b/vcl/osx/res/cursors/movef2.png
new file mode 100644
index 000000000000..2cdddb410aae
--- /dev/null
+++ b/vcl/osx/res/cursors/movef2.png
Binary files differ
diff --git a/vcl/osx/res/cursors/moveflnk.png b/vcl/osx/res/cursors/moveflnk.png
new file mode 100644
index 000000000000..53301ba58c50
--- /dev/null
+++ b/vcl/osx/res/cursors/moveflnk.png
Binary files differ
diff --git a/vcl/osx/res/cursors/movept.png b/vcl/osx/res/cursors/movept.png
new file mode 100644
index 000000000000..41945deb1916
--- /dev/null
+++ b/vcl/osx/res/cursors/movept.png
Binary files differ
diff --git a/vcl/osx/res/cursors/neswsize.png b/vcl/osx/res/cursors/neswsize.png
new file mode 100644
index 000000000000..91b89b5803ec
--- /dev/null
+++ b/vcl/osx/res/cursors/neswsize.png
Binary files differ
diff --git a/vcl/osx/res/cursors/notallow.png b/vcl/osx/res/cursors/notallow.png
new file mode 100644
index 000000000000..df770a495194
--- /dev/null
+++ b/vcl/osx/res/cursors/notallow.png
Binary files differ
diff --git a/vcl/osx/res/cursors/nullptr.png b/vcl/osx/res/cursors/nullptr.png
new file mode 100644
index 000000000000..489636595bec
--- /dev/null
+++ b/vcl/osx/res/cursors/nullptr.png
Binary files differ
diff --git a/vcl/osx/res/cursors/nwsesize.png b/vcl/osx/res/cursors/nwsesize.png
new file mode 100644
index 000000000000..fc6a33288ef2
--- /dev/null
+++ b/vcl/osx/res/cursors/nwsesize.png
Binary files differ
diff --git a/vcl/osx/res/cursors/pen.png b/vcl/osx/res/cursors/pen.png
new file mode 100644
index 000000000000..81b583086778
--- /dev/null
+++ b/vcl/osx/res/cursors/pen.png
Binary files differ
diff --git a/vcl/osx/res/cursors/pivotcol.png b/vcl/osx/res/cursors/pivotcol.png
new file mode 100644
index 000000000000..1c38b915b886
--- /dev/null
+++ b/vcl/osx/res/cursors/pivotcol.png
Binary files differ
diff --git a/vcl/osx/res/cursors/pivotdel.png b/vcl/osx/res/cursors/pivotdel.png
new file mode 100644
index 000000000000..fbd663ee36c1
--- /dev/null
+++ b/vcl/osx/res/cursors/pivotdel.png
Binary files differ
diff --git a/vcl/osx/res/cursors/pivotfld.png b/vcl/osx/res/cursors/pivotfld.png
new file mode 100644
index 000000000000..04375de1efe6
--- /dev/null
+++ b/vcl/osx/res/cursors/pivotfld.png
Binary files differ
diff --git a/vcl/osx/res/cursors/pivotrow.png b/vcl/osx/res/cursors/pivotrow.png
new file mode 100644
index 000000000000..18ef0e8e59ba
--- /dev/null
+++ b/vcl/osx/res/cursors/pivotrow.png
Binary files differ
diff --git a/vcl/osx/res/cursors/pntbrsh.png b/vcl/osx/res/cursors/pntbrsh.png
new file mode 100644
index 000000000000..ec8d799f66c2
--- /dev/null
+++ b/vcl/osx/res/cursors/pntbrsh.png
Binary files differ
diff --git a/vcl/osx/res/cursors/rotate.png b/vcl/osx/res/cursors/rotate.png
new file mode 100644
index 000000000000..a8137e077e56
--- /dev/null
+++ b/vcl/osx/res/cursors/rotate.png
Binary files differ
diff --git a/vcl/osx/res/cursors/tblsele.png b/vcl/osx/res/cursors/tblsele.png
new file mode 100644
index 000000000000..a2da05e009d1
--- /dev/null
+++ b/vcl/osx/res/cursors/tblsele.png
Binary files differ
diff --git a/vcl/osx/res/cursors/tblsels.png b/vcl/osx/res/cursors/tblsels.png
new file mode 100644
index 000000000000..ba20589c794f
--- /dev/null
+++ b/vcl/osx/res/cursors/tblsels.png
Binary files differ
diff --git a/vcl/osx/res/cursors/tblselse.png b/vcl/osx/res/cursors/tblselse.png
new file mode 100644
index 000000000000..4ee7f62b304b
--- /dev/null
+++ b/vcl/osx/res/cursors/tblselse.png
Binary files differ
diff --git a/vcl/osx/res/cursors/tblselsw.png b/vcl/osx/res/cursors/tblselsw.png
new file mode 100644
index 000000000000..11d7cb34d64d
--- /dev/null
+++ b/vcl/osx/res/cursors/tblselsw.png
Binary files differ
diff --git a/vcl/osx/res/cursors/tblselw.png b/vcl/osx/res/cursors/tblselw.png
new file mode 100644
index 000000000000..62813a975855
--- /dev/null
+++ b/vcl/osx/res/cursors/tblselw.png
Binary files differ
diff --git a/vcl/osx/res/cursors/timemove.png b/vcl/osx/res/cursors/timemove.png
new file mode 100644
index 000000000000..3dae038a2011
--- /dev/null
+++ b/vcl/osx/res/cursors/timemove.png
Binary files differ
diff --git a/vcl/osx/res/cursors/timesize.png b/vcl/osx/res/cursors/timesize.png
new file mode 100644
index 000000000000..22178c9b8d0e
--- /dev/null
+++ b/vcl/osx/res/cursors/timesize.png
Binary files differ
diff --git a/vcl/osx/res/cursors/vshear.png b/vcl/osx/res/cursors/vshear.png
new file mode 100644
index 000000000000..b01cb6c935e1
--- /dev/null
+++ b/vcl/osx/res/cursors/vshear.png
Binary files differ
diff --git a/vcl/osx/res/cursors/vtext.png b/vcl/osx/res/cursors/vtext.png
new file mode 100644
index 000000000000..2d6c847c9fc7
--- /dev/null
+++ b/vcl/osx/res/cursors/vtext.png
Binary files differ
diff --git a/vcl/osx/saldata.cxx b/vcl/osx/saldata.cxx
new file mode 100644
index 000000000000..55a5e6493782
--- /dev/null
+++ b/vcl/osx/saldata.cxx
@@ -0,0 +1,265 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#include "osx/saldata.hxx"
+#include "osx/salnsmenu.h"
+#include "osx/salinst.h"
+
+#import "apple_remote/RemoteMainController.h"
+
+oslThreadKey SalData::s_aAutoReleaseKey = 0;
+
+static void SAL_CALL releasePool( void* pPool )
+{
+ if( pPool )
+ [(NSAutoreleasePool*)pPool release];
+}
+
+SalData::SalData()
+:
+ mpTimerProc( NULL ),
+ mpFirstInstance( NULL ),
+ mpFirstObject( NULL ),
+ mpFirstVD( NULL ),
+ mpFirstPrinter( NULL ),
+ mpFontList( NULL ),
+ mpStatusItem( nil ),
+ mxRGBSpace( CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB) ),
+ mxGraySpace( CGColorSpaceCreateWithName(kCGColorSpaceGenericGray) ),
+ mxP50Space( NULL ),
+ mxP50Pattern( NULL ),
+ maCursors( POINTER_COUNT, INVALID_CURSOR_PTR ),
+ mbIsScrollbarDoubleMax( false ),
+#if !HAVE_FEATURE_MACOSX_SANDBOX
+ mpMainController( NULL ),
+#endif
+ mpDockIconClickHandler( nil ),
+ mnDPIX( 0 ),
+ mnDPIY( 0 )
+{
+ if( s_aAutoReleaseKey == 0 )
+ s_aAutoReleaseKey = osl_createThreadKey( releasePool );
+}
+
+SalData::~SalData()
+{
+ CGPatternRelease( mxP50Pattern );
+ CGColorSpaceRelease( mxP50Space );
+ CGColorSpaceRelease( mxRGBSpace );
+ CGColorSpaceRelease( mxGraySpace );
+ for( unsigned int i = 0; i < maCursors.size(); i++ )
+ {
+ NSCursor* pCurs = maCursors[i];
+ if( pCurs && pCurs != INVALID_CURSOR_PTR )
+ [pCurs release];
+ }
+ if( s_aAutoReleaseKey )
+ {
+ // release the last pool
+ NSAutoreleasePool* pPool = nil;
+ pPool = reinterpret_cast<NSAutoreleasePool*>( osl_getThreadKeyData( s_aAutoReleaseKey ) );
+ if( pPool )
+ {
+ osl_setThreadKeyData( s_aAutoReleaseKey, NULL );
+ [pPool release];
+ }
+
+ osl_destroyThreadKey( s_aAutoReleaseKey );
+ s_aAutoReleaseKey = 0;
+ }
+#if !HAVE_FEATURE_MACOSX_SANDBOX
+ if ( mpMainController )
+ [mpMainController release];
+#endif
+}
+
+void SalData::ensureThreadAutoreleasePool()
+{
+ NSAutoreleasePool* pPool = nil;
+ if( s_aAutoReleaseKey )
+ {
+ pPool = reinterpret_cast<NSAutoreleasePool*>( osl_getThreadKeyData( s_aAutoReleaseKey ) );
+ if( ! pPool )
+ {
+ pPool = [[NSAutoreleasePool alloc] init];
+ osl_setThreadKeyData( s_aAutoReleaseKey, pPool );
+ }
+ }
+ else
+ {
+ OSL_FAIL( "no autorelease key" );
+ }
+}
+
+struct curs_ent
+{
+ const char* pBaseName;
+ const NSPoint aHotSpot;
+}
+const aCursorTab[ POINTER_COUNT ] =
+{
+{ NULL, { 0, 0 } }, //POINTER_ARROW
+{ "nullptr", { 16, 16 } }, //POINTER_NULL
+{ "hourglass", { 15, 15 } }, //POINTER_WAIT
+{ NULL, { 0, 0 } }, //POINTER_TEXT
+{ "help", { 0, 0 } }, //POINTER_HELP
+{ NULL, { 0, 0 } }, //POINTER_CROSS
+{ NULL, { 0, 0 } }, //POINTER_MOVE
+{ NULL, { 0, 0 } }, //POINTER_NSIZE
+{ NULL, { 0, 0 } }, //POINTER_SSIZE
+{ NULL, { 0, 0 } }, //POINTER_WSIZE
+{ NULL, { 0, 0 } }, //POINTER_ESIZE
+{ "nwsesize", { 15, 15 } }, //POINTER_NWSIZE
+{ "neswsize", { 15, 15 } }, //POINTER_NESIZE
+{ "neswsize", { 15, 15 } }, //POINTER_SWSIZE
+{ "nwsesize", { 15, 15 } }, //POINTER_SESIZE
+{ NULL, { 0, 0 } }, //POINTER_WINDOW_NSIZE
+{ NULL, { 0, 0 } }, //POINTER_WINDOW_SSIZE
+{ NULL, { 0, 0 } }, //POINTER_WINDOW_WSIZE
+{ NULL, { 0, 0 } }, //POINTER_WINDOW_ESIZE
+{ "nwsesize", { 15, 15 } }, //POINTER_WINDOW_NWSIZE
+{ "neswsize", { 15, 15 } }, //POINTER_WINDOW_NESIZE
+{ "neswsize", { 15, 15 } }, //POINTER_WINDOW_SWSIZE
+{ "nwsesize", { 15, 15 } }, //POINTER_WINDOW_SESIZE
+{ NULL, { 0, 0 } }, //POINTER_HSPLIT
+{ NULL, { 0, 0 } }, //POINTER_VSPLIT
+{ NULL, { 0, 0 } }, //POINTER_HSIZEBAR
+{ NULL, { 0, 0 } }, //POINTER_VSIZEBAR
+{ NULL, { 0, 0 } }, //POINTER_HAND
+{ NULL, { 0, 0 } }, //POINTER_REFHAND
+{ "pen", { 3, 27 } }, //POINTER_PEN
+{ "magnify", { 12, 13 } }, //POINTER_MAGNIFY
+{ "fill", { 10, 22 } }, //POINTER_FILL
+{ "rotate", { 15, 15 } }, //POINTER_ROTATE
+{ "hshear", { 15, 15 } }, //POINTER_HSHEAR
+{ "vshear", { 15, 15 } }, //POINTER_VSHEAR
+{ "mirror", { 14, 12 } }, //POINTER_MIRROR
+{ "crook", { 15, 14 } }, //POINTER_CROOK
+{ "crop", { 9, 9 } }, //POINTER_CROP
+{ "movept", { 0, 0 } }, //POINTER_MOVEPOINT
+{ "movebw", { 0, 0 } }, //POINTER_MOVEBEZIERWEIGHT
+{ "movedata", { 0, 0 } }, //POINTER_MOVEDATA
+{ "copydata", { 0, 0 } }, //POINTER_COPYDATA
+{ "linkdata", { 0, 0 } }, //POINTER_LINKDATA
+{ "movedlnk", { 0, 0 } }, //POINTER_MOVEDATALINK
+{ "copydlnk", { 0, 0 } }, //POINTER_COPYDATALINK
+{ "movef", { 8, 8 } }, //POINTER_MOVEFILE
+{ "copyf", { 8, 8 } }, //POINTER_COPYFILE
+{ "linkf", { 8, 8 } }, //POINTER_LINKFILE
+{ "moveflnk", { 8, 8 } }, //POINTER_MOVEFILELINK
+{ "copyflnk", { 8, 8 } }, //POINTER_COPYFILELINK
+{ "movef2", { 7, 8 } }, //POINTER_MOVEFILES
+{ "copyf2", { 7, 8 } }, //POINTER_COPYFILES
+{ "notallow", { 15, 15 } }, //POINTER_NOTALLOWED
+{ "dline", { 8, 8 } }, //POINTER_DRAW_LINE
+{ "drect", { 8, 8 } }, //POINTER_DRAW_RECT
+{ "dpolygon", { 8, 8 } }, //POINTER_DRAW_POLYGON
+{ "dbezier", { 8, 8 } }, //POINTER_DRAW_BEZIER
+{ "darc", { 8, 8 } }, //POINTER_DRAW_ARC
+{ "dpie", { 8, 8 } }, //POINTER_DRAW_PIE
+{ "dcirccut", { 8, 8 } }, //POINTER_DRAW_CIRCLECUT
+{ "dellipse", { 8, 8 } }, //POINTER_DRAW_ELLIPSE
+{ "dfree", { 8, 8 } }, //POINTER_DRAW_FREEHAND
+{ "dconnect", { 8, 8 } }, //POINTER_DRAW_CONNECT
+{ "dtext", { 8, 8 } }, //POINTER_DRAW_TEXT
+{ "dcapt", { 8, 8 } }, //POINTER_DRAW_CAPTION
+{ "chart", { 15, 16 } }, //POINTER_CHART
+{ "detectiv", { 12, 13 } }, //POINTER_DETECTIVE
+{ "pivotcol", { 7, 5 } }, //POINTER_PIVOT_COL
+{ "pivotrow", { 8, 7 } }, //POINTER_PIVOT_ROW
+{ "pivotfld", { 8, 7 } }, //POINTER_PIVOT_FIELD
+{ "chain", { 0, 2 } }, //POINTER_CHAIN
+{ "chainnot", { 2, 2 } }, //POINTER_CHAIN_NOTALLOWED
+{ "timemove", { 16, 16 } }, //POINTER_TIMEEVENT_MOVE
+{ "timesize", { 16, 17 } }, //POINTER_TIMEEVENT_SIZE
+{ "asn", { 16, 12 } }, //POINTER_AUTOSCROLL_N
+{ "ass", { 15, 19 } }, //POINTER_AUTOSCROLL_S
+{ "asw", { 12, 15 } }, //POINTER_AUTOSCROLL_W
+{ "ase", { 19, 16 } }, //POINTER_AUTOSCROLL_E
+{ "asnw", { 10, 10 } }, //POINTER_AUTOSCROLL_NW
+{ "asne", { 21, 10 } }, //POINTER_AUTOSCROLL_NE
+{ "assw", { 21, 21 } }, //POINTER_AUTOSCROLL_SW
+{ "asse", { 21, 21 } }, //POINTER_AUTOSCROLL_SE
+{ "asns", { 15, 15 } }, //POINTER_AUTOSCROLL_NS
+{ "aswe", { 15, 15 } }, //POINTER_AUTOSCROLL_WE
+{ "asnswe", { 15, 15 } }, //POINTER_AUTOSCROLL_NSWE
+{ "airbrush", { 5, 22 } }, //POINTER_AIRBRUSH
+{ "vtext", { 15, 15 } }, //POINTER_TEXT_VERTICAL
+{ "pivotdel", { 18, 15 } }, //POINTER_PIVOT_DELETE
+{ "tblsels", { 15, 30 } }, //POINTER_TAB_SELECT_S
+{ "tblsele", { 30, 16 } }, //POINTER_TAB_SELECT_E
+{ "tblselse", { 30, 30 } }, //POINTER_TAB_SELECT_SE
+{ "tblselw", { 1, 16 } }, //POINTER_TAB_SELECT_W
+{ "tblselsw", { 1, 30 } }, //POINTER_TAB_SELECT_SW
+{ "pntbrsh", { 9, 16 } } //POINTER_PAINTBRUSH
+};
+
+NSCursor* SalData::getCursor( PointerStyle i_eStyle )
+{
+ if( i_eStyle >= POINTER_COUNT )
+ return nil;
+
+ NSCursor* pCurs = maCursors[ i_eStyle ];
+ if( pCurs == INVALID_CURSOR_PTR )
+ {
+ pCurs = nil;
+ if( aCursorTab[ i_eStyle ].pBaseName )
+ {
+ NSPoint aHotSpot = aCursorTab[ i_eStyle ].aHotSpot;
+ CFStringRef pCursorName =
+ CFStringCreateWithCStringNoCopy(
+ kCFAllocatorDefault,
+ aCursorTab[ i_eStyle ].pBaseName,
+ kCFStringEncodingASCII,
+ kCFAllocatorNull );
+ CFBundleRef hMain = CFBundleGetMainBundle();
+ CFURLRef hURL = CFBundleCopyResourceURL( hMain, pCursorName, CFSTR("png"), CFSTR("cursors") );
+ if( hURL )
+ {
+ pCurs = [[NSCursor alloc] initWithImage: [[NSImage alloc] initWithContentsOfURL: (NSURL*)hURL] hotSpot: aHotSpot];
+ CFRelease( hURL );
+ }
+ CFRelease( pCursorName );
+ }
+ maCursors[ i_eStyle ] = pCurs;
+ }
+ return pCurs;
+}
+
+NSStatusItem* SalData::getStatusItem()
+{
+ SalData* pData = GetSalData();
+ if( ! pData->mpStatusItem )
+ {
+ NSStatusBar* pStatBar =[NSStatusBar systemStatusBar];
+ if( pStatBar )
+ {
+ pData->mpStatusItem = [pStatBar statusItemWithLength: NSVariableStatusItemLength];
+ [pData->mpStatusItem retain];
+ OOStatusItemView* pView = [[OOStatusItemView alloc] init];
+ [pData->mpStatusItem setView: pView ];
+ [pView display];
+ }
+ }
+ return pData->mpStatusItem;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/salframe.cxx b/vcl/osx/salframe.cxx
new file mode 100644
index 000000000000..cf1fe45b97e3
--- /dev/null
+++ b/vcl/osx/salframe.cxx
@@ -0,0 +1,1769 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <string>
+
+#include "rtl/ustrbuf.hxx"
+
+#include "osl/file.h"
+
+#include "vcl/svapp.hxx"
+#include "vcl/window.hxx"
+#include "vcl/syswin.hxx"
+
+#include "osx/saldata.hxx"
+#include "quartz/salgdi.h"
+#include "osx/salframe.h"
+#include "osx/salmenu.h"
+#include "osx/salinst.h"
+#include "osx/salframeview.h"
+#include "osx/a11yfactory.h"
+#include "quartz/utils.h"
+
+#include "salwtype.hxx"
+
+#include "premac.h"
+#include <objc/objc-runtime.h>
+// needed for theming
+// FIXME: move theming code to salnativewidgets.cxx
+#include <Carbon/Carbon.h>
+#include "postmac.h"
+
+using namespace std;
+
+// =======================================================================
+
+AquaSalFrame* AquaSalFrame::s_pCaptureFrame = NULL;
+
+// =======================================================================
+
+AquaSalFrame::AquaSalFrame( SalFrame* pParent, sal_uLong salFrameStyle ) :
+ mpNSWindow(nil),
+ mpNSView(nil),
+ mpDockMenuEntry(nil),
+ mpGraphics(NULL),
+ mpParent(NULL),
+ mnMinWidth(0),
+ mnMinHeight(0),
+ mnMaxWidth(0),
+ mnMaxHeight(0),
+ mbGraphics(false),
+ mbFullScreen( false ),
+ mbShown(false),
+ mbInitShow(true),
+ mbPositioned(false),
+ mbSized(false),
+ mbPresentation( false ),
+ mnStyle( salFrameStyle ),
+ mnStyleMask( 0 ),
+ mnLastEventTime( 0 ),
+ mnLastModifierFlags( 0 ),
+ mpMenu( NULL ),
+ mnExtStyle( 0 ),
+ mePointerStyle( POINTER_ARROW ),
+ mnTrackingRectTag( 0 ),
+ mrClippingPath( 0 ),
+ mnICOptions( 0 )
+{
+ maSysData.nSize = sizeof( SystemEnvData );
+
+ mpParent = dynamic_cast<AquaSalFrame*>(pParent);
+
+ initWindowAndView();
+
+ SalData* pSalData = GetSalData();
+ pSalData->maFrames.push_front( this );
+ pSalData->maFrameCheck.insert( this );
+}
+
+// -----------------------------------------------------------------------
+
+AquaSalFrame::~AquaSalFrame()
+{
+ // if the frame is destroyed and has the current menubar
+ // set the default menubar
+ if( mpMenu && mpMenu->mbMenuBar && AquaSalMenu::pCurrentMenuBar == mpMenu )
+ AquaSalMenu::setDefaultMenu();
+
+ // cleanup clipping stuff
+ ResetClipRegion();
+
+ [SalFrameView unsetMouseFrame: this];
+
+ SalData* pSalData = GetSalData();
+ pSalData->maFrames.remove( this );
+ pSalData->maFrameCheck.erase( this );
+ pSalData->maPresentationFrames.remove( this );
+
+ DBG_ASSERT( this != s_pCaptureFrame, "capture frame destroyed" );
+ if( this == s_pCaptureFrame )
+ s_pCaptureFrame = NULL;
+
+ delete mpGraphics;
+
+ if( mpDockMenuEntry )
+ // life cycle comment: the menu has ownership of the item, so no release
+ [AquaSalInstance::GetDynamicDockMenu() removeItem: mpDockMenuEntry];
+ if ( mpNSView ) {
+ [AquaA11yFactory revokeView: mpNSView];
+ [mpNSView release];
+ }
+ if ( mpNSWindow )
+ [mpNSWindow release];
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::initWindowAndView()
+{
+ // initialize mirroring parameters
+ // FIXME: screens changing
+ NSScreen* pNSScreen = [mpNSWindow screen];
+ if( pNSScreen == nil )
+ pNSScreen = [NSScreen mainScreen];
+ maScreenRect = [pNSScreen frame];
+
+ // calculate some default geometry
+ NSRect aVisibleRect = [pNSScreen visibleFrame];
+ CocoaToVCL( aVisibleRect );
+
+ maGeometry.nX = static_cast<int>(aVisibleRect.origin.x + aVisibleRect.size.width / 10);
+ maGeometry.nY = static_cast<int>(aVisibleRect.origin.y + aVisibleRect.size.height / 10);
+ maGeometry.nWidth = static_cast<unsigned int>(aVisibleRect.size.width * 0.8);
+ maGeometry.nHeight = static_cast<unsigned int>(aVisibleRect.size.height * 0.8);
+
+ // calculate style mask
+ if( (mnStyle & SAL_FRAME_STYLE_FLOAT) ||
+ (mnStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) )
+ mnStyleMask = NSBorderlessWindowMask;
+ else if( mnStyle & SAL_FRAME_STYLE_DEFAULT )
+ {
+ mnStyleMask = NSTitledWindowMask |
+ NSMiniaturizableWindowMask |
+ NSResizableWindowMask |
+ NSClosableWindowMask;
+ // make default window "maximized"
+ maGeometry.nX = static_cast<int>(aVisibleRect.origin.x);
+ maGeometry.nY = static_cast<int>(aVisibleRect.origin.y);
+ maGeometry.nWidth = static_cast<int>(aVisibleRect.size.width);
+ maGeometry.nHeight = static_cast<int>(aVisibleRect.size.height);
+ mbPositioned = mbSized = true;
+ }
+ else
+ {
+ if( (mnStyle & SAL_FRAME_STYLE_MOVEABLE) )
+ {
+ mnStyleMask |= NSTitledWindowMask;
+ if( mpParent == NULL )
+ mnStyleMask |= NSMiniaturizableWindowMask;
+ }
+ if( (mnStyle & SAL_FRAME_STYLE_SIZEABLE) )
+ mnStyleMask |= NSResizableWindowMask;
+ if( (mnStyle & SAL_FRAME_STYLE_CLOSEABLE) )
+ mnStyleMask |= NSClosableWindowMask;
+ // documentation says anything other than NSBorderlessWindowMask (=0)
+ // should also include NSTitledWindowMask;
+ if( mnStyleMask != 0 )
+ mnStyleMask |= NSTitledWindowMask;
+ }
+
+ // #i91990# support GUI-less (daemon) execution
+ @try
+ {
+ mpNSWindow = [[SalFrameWindow alloc] initWithSalFrame: this];
+ mpNSView = [[SalFrameView alloc] initWithSalFrame: this];
+ }
+ @catch ( id exception )
+ {
+ return;
+ }
+
+ if( (mnStyle & SAL_FRAME_STYLE_TOOLTIP) )
+ [mpNSWindow setIgnoresMouseEvents: YES];
+ else
+ [mpNSWindow setAcceptsMouseMovedEvents: YES];
+ [mpNSWindow setHasShadow: YES];
+
+ // WTF? With the 10.6 SDK and gcc 4.2.1, we get: class 'NSWindow'
+ // does not implement the 'NSWindowDelegate' protocol. Anyway,
+ // having the window object be its own delegate object is
+ // apparently what the code does on purpose, see discussion in
+ // https://issues.apache.org/ooo/show_bug.cgi?id=91990
+
+ // So to silence the warning when compiling with -Werror, instead of:
+ // [mpNSWindow setDelegate: mpNSWindow];
+ // do this:
+ objc_msgSend(mpNSWindow, @selector(setDelegate:), mpNSWindow);
+
+ if( [mpNSWindow respondsToSelector: @selector(setRestorable:)])
+ {
+ objc_msgSend(mpNSWindow, @selector(setRestorable:), NO);
+ }
+ NSRect aRect = { { 0,0 }, { static_cast<CGFloat>(maGeometry.nWidth), static_cast<CGFloat>(maGeometry.nHeight) } };
+ mnTrackingRectTag = [mpNSView addTrackingRect: aRect owner: mpNSView userData: nil assumeInside: NO];
+
+ maSysData.mpNSView = mpNSView;
+
+ UpdateFrameGeometry();
+
+ [mpNSWindow setContentView: mpNSView];
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::CocoaToVCL( NSRect& io_rRect, bool bRelativeToScreen )
+{
+ if( bRelativeToScreen )
+ io_rRect.origin.y = maScreenRect.size.height - (io_rRect.origin.y+io_rRect.size.height);
+ else
+ io_rRect.origin.y = maGeometry.nHeight - (io_rRect.origin.y+io_rRect.size.height);
+}
+
+void AquaSalFrame::VCLToCocoa( NSRect& io_rRect, bool bRelativeToScreen )
+{
+ if( bRelativeToScreen )
+ io_rRect.origin.y = maScreenRect.size.height - (io_rRect.origin.y+io_rRect.size.height);
+ else
+ io_rRect.origin.y = maGeometry.nHeight - (io_rRect.origin.y+io_rRect.size.height);
+}
+
+void AquaSalFrame::CocoaToVCL( NSPoint& io_rPoint, bool bRelativeToScreen )
+{
+ if( bRelativeToScreen )
+ io_rPoint.y = maScreenRect.size.height - io_rPoint.y;
+ else
+ io_rPoint.y = maGeometry.nHeight - io_rPoint.y;
+}
+
+void AquaSalFrame::VCLToCocoa( NSPoint& io_rPoint, bool bRelativeToScreen )
+{
+ if( bRelativeToScreen )
+ io_rPoint.y = maScreenRect.size.height - io_rPoint.y;
+ else
+ io_rPoint.y = maGeometry.nHeight - io_rPoint.y;
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::screenParametersChanged()
+{
+ UpdateFrameGeometry();
+
+ if( mpGraphics )
+ mpGraphics->updateResolution();
+ CallCallback( SALEVENT_DISPLAYCHANGED, 0 );
+}
+
+// -----------------------------------------------------------------------
+
+SalGraphics* AquaSalFrame::GetGraphics()
+{
+ if ( mbGraphics )
+ return NULL;
+
+ if ( !mpGraphics )
+ {
+ mpGraphics = new AquaSalGraphics;
+ mpGraphics->SetWindowGraphics( this );
+ }
+
+ mbGraphics = TRUE;
+ return mpGraphics;
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::ReleaseGraphics( SalGraphics *pGraphics )
+{
+ (void)pGraphics;
+ DBG_ASSERT( pGraphics == mpGraphics, "graphics released on wrong frame" );
+ mbGraphics = FALSE;
+}
+
+// -----------------------------------------------------------------------
+
+sal_Bool AquaSalFrame::PostEvent( void *pData )
+{
+ GetSalData()->mpFirstInstance->PostUserEvent( this, SALEVENT_USEREVENT, pData );
+ return TRUE;
+}
+
+// -----------------------------------------------------------------------
+void AquaSalFrame::SetTitle(const OUString& rTitle)
+{
+ if ( !mpNSWindow )
+ return;
+
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ NSString* pTitle = CreateNSString( rTitle );
+ [mpNSWindow setTitle: pTitle];
+
+ // create an entry in the dock menu
+ const sal_uLong nAppWindowStyle = (SAL_FRAME_STYLE_CLOSEABLE | SAL_FRAME_STYLE_MOVEABLE);
+ if( mpParent == NULL &&
+ (mnStyle & nAppWindowStyle) == nAppWindowStyle )
+ {
+ if( mpDockMenuEntry == NULL )
+ {
+ NSMenu* pDock = AquaSalInstance::GetDynamicDockMenu();
+ mpDockMenuEntry = [pDock insertItemWithTitle: pTitle
+ action: @selector(dockMenuItemTriggered:)
+ keyEquivalent: @""
+ atIndex: 0];
+ [mpDockMenuEntry setTarget: mpNSWindow];
+
+ // TODO: image (either the generic window image or an icon
+ // check mark (for "main" window ?)
+ }
+ else
+ [mpDockMenuEntry setTitle: pTitle];
+ }
+
+ if (pTitle)
+ [pTitle release];
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::SetIcon( sal_uInt16 )
+{
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::SetRepresentedURL( const OUString& i_rDocURL )
+{
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ if( i_rDocURL.startsWith( "file:" ) )
+ {
+ OUString aSysPath;
+ osl_getSystemPathFromFileURL( i_rDocURL.pData, &aSysPath.pData );
+ NSString* pStr = CreateNSString( aSysPath );
+ if( pStr )
+ {
+ [pStr autorelease];
+ [mpNSWindow setRepresentedFilename: pStr];
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::initShow()
+{
+ mbInitShow = false;
+ if( ! mbPositioned && ! mbFullScreen )
+ {
+ Rectangle aScreenRect;
+ GetWorkArea( aScreenRect );
+ if( mpParent ) // center relative to parent
+ {
+ // center on parent
+ long nNewX = mpParent->maGeometry.nX + ((long)mpParent->maGeometry.nWidth - (long)maGeometry.nWidth)/2;
+ if( nNewX < aScreenRect.Left() )
+ nNewX = aScreenRect.Left();
+ if( long(nNewX + maGeometry.nWidth) > aScreenRect.Right() )
+ nNewX = aScreenRect.Right() - maGeometry.nWidth-1;
+ long nNewY = mpParent->maGeometry.nY + ((long)mpParent->maGeometry.nHeight - (long)maGeometry.nHeight)/2;
+ if( nNewY < aScreenRect.Top() )
+ nNewY = aScreenRect.Top();
+ if( nNewY > aScreenRect.Bottom() )
+ nNewY = aScreenRect.Bottom() - maGeometry.nHeight-1;
+ SetPosSize( nNewX - mpParent->maGeometry.nX,
+ nNewY - mpParent->maGeometry.nY,
+ 0, 0, SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y );
+ }
+ else if( ! (mnStyle & SAL_FRAME_STYLE_SIZEABLE) )
+ {
+ // center on screen
+ long nNewX = (aScreenRect.GetWidth() - maGeometry.nWidth)/2;
+ long nNewY = (aScreenRect.GetHeight() - maGeometry.nHeight)/2;
+ SetPosSize( nNewX, nNewY, 0, 0, SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y );
+ }
+ }
+
+ // make sure the view is present in the wrapper list before any children receive focus
+ [AquaA11yFactory registerView: mpNSView];
+}
+
+void AquaSalFrame::SendPaintEvent( const Rectangle* pRect )
+{
+ SalPaintEvent aPaintEvt( 0, 0, maGeometry.nWidth, maGeometry.nHeight, true );
+ if( pRect )
+ {
+ aPaintEvt.mnBoundX = pRect->Left();
+ aPaintEvt.mnBoundY = pRect->Top();
+ aPaintEvt.mnBoundWidth = pRect->GetWidth();
+ aPaintEvt.mnBoundHeight = pRect->GetHeight();
+ }
+
+ CallCallback(SALEVENT_PAINT, &aPaintEvt);
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::Show(sal_Bool bVisible, sal_Bool bNoActivate)
+{
+ if ( !mpNSWindow )
+ return;
+
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ mbShown = bVisible;
+ if(bVisible)
+ {
+ if( mbInitShow )
+ initShow();
+
+ CallCallback(SALEVENT_RESIZE, 0);
+ // trigger filling our backbuffer
+ SendPaintEvent();
+
+ if( bNoActivate || [mpNSWindow canBecomeKeyWindow] == NO )
+ [mpNSWindow orderFront: NSApp];
+ else
+ [mpNSWindow makeKeyAndOrderFront: NSApp];
+
+ if( mpParent )
+ {
+ /* #i92674# #i96433# we do not want an invisible parent to show up (which adding a visible
+ child implicitly does). However we also do not want a parentless toolbar.
+
+ HACK: try to decide when we should not insert a child to its parent
+ floaters and ownerdraw windows have not yet shown up in cases where
+ we don't want the parent to become visible
+ */
+ if( mpParent->mbShown || (mnStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION | SAL_FRAME_STYLE_FLOAT) ) )
+ {
+ [mpParent->mpNSWindow addChildWindow: mpNSWindow ordered: NSWindowAbove];
+ }
+ }
+
+ if( mbPresentation )
+ [mpNSWindow makeMainWindow];
+ }
+ else
+ {
+ // if the frame holding the current menubar gets hidden
+ // show the default menubar
+ if( mpMenu && mpMenu->mbMenuBar && AquaSalMenu::pCurrentMenuBar == mpMenu )
+ AquaSalMenu::setDefaultMenu();
+
+ // #i90440# #i94443# work around the focus going back to some other window
+ // if a child gets hidden for a parent window
+ if( mpParent && mpParent->mbShown && [mpNSWindow isKeyWindow] )
+ [mpParent->mpNSWindow makeKeyAndOrderFront: NSApp];
+
+ [SalFrameView unsetMouseFrame: this];
+ if( mpParent && [mpNSWindow parentWindow] == mpParent->mpNSWindow )
+ [mpParent->mpNSWindow removeChildWindow: mpNSWindow];
+
+ [mpNSWindow orderOut: NSApp];
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::Enable( sal_Bool )
+{
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::SetMinClientSize( long nWidth, long nHeight )
+{
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ mnMinWidth = nWidth;
+ mnMinHeight = nHeight;
+
+ if( mpNSWindow )
+ {
+ // Always add the decoration as the dimension concerns only
+ // the content rectangle
+ nWidth += maGeometry.nLeftDecoration + maGeometry.nRightDecoration;
+ nHeight += maGeometry.nTopDecoration + maGeometry.nBottomDecoration;
+
+ NSSize aSize = { static_cast<CGFloat>(nWidth), static_cast<CGFloat>(nHeight) };
+
+ // Size of full window (content+structure) although we only
+ // have the client size in arguments
+ [mpNSWindow setMinSize: aSize];
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::SetMaxClientSize( long nWidth, long nHeight )
+{
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ mnMaxWidth = nWidth;
+ mnMaxHeight = nHeight;
+
+ if( mpNSWindow )
+ {
+ // Always add the decoration as the dimension concerns only
+ // the content rectangle
+ nWidth += maGeometry.nLeftDecoration + maGeometry.nRightDecoration;
+ nHeight += maGeometry.nTopDecoration + maGeometry.nBottomDecoration;
+
+ // Carbon windows can't have a size greater than 32767x32767
+ if (nWidth>32767) nWidth=32767;
+ if (nHeight>32767) nHeight=32767;
+
+ NSSize aSize = { static_cast<CGFloat>(nWidth), static_cast<CGFloat>(nHeight) };
+
+ // Size of full window (content+structure) although we only
+ // have the client size in arguments
+ [mpNSWindow setMaxSize: aSize];
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::SetClientSize( long nWidth, long nHeight )
+{
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ if( mpNSWindow )
+ {
+ NSSize aSize = { static_cast<CGFloat>(nWidth), static_cast<CGFloat>(nHeight) };
+
+ [mpNSWindow setContentSize: aSize];
+ UpdateFrameGeometry();
+ if( mbShown )
+ // trigger filling our backbuffer
+ SendPaintEvent();
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::GetClientSize( long& rWidth, long& rHeight )
+{
+ if( mbShown || mbInitShow )
+ {
+ rWidth = maGeometry.nWidth;
+ rHeight = maGeometry.nHeight;
+ }
+ else
+ {
+ rWidth = 0;
+ rHeight = 0;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::SetWindowState( const SalFrameState* pState )
+{
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ if ( mpNSWindow )
+ {
+ // set normal state
+ NSRect aStateRect = [mpNSWindow frame];
+ aStateRect = [NSWindow contentRectForFrameRect: aStateRect styleMask: mnStyleMask];
+ CocoaToVCL( aStateRect );
+ if( pState->mnMask & WINDOWSTATE_MASK_X )
+ aStateRect.origin.x = float(pState->mnX);
+ if( pState->mnMask & WINDOWSTATE_MASK_Y )
+ aStateRect.origin.y = float(pState->mnY);
+ if( pState->mnMask & WINDOWSTATE_MASK_WIDTH )
+ aStateRect.size.width = float(pState->mnWidth);
+ if( pState->mnMask & WINDOWSTATE_MASK_HEIGHT )
+ aStateRect.size.height = float(pState->mnHeight);
+ VCLToCocoa( aStateRect );
+ aStateRect = [NSWindow frameRectForContentRect: aStateRect styleMask: mnStyleMask];
+
+ [mpNSWindow setFrame: aStateRect display: NO];
+ if( pState->mnState == WINDOWSTATE_STATE_MINIMIZED )
+ [mpNSWindow miniaturize: NSApp];
+ else if( [mpNSWindow isMiniaturized] )
+ [mpNSWindow deminiaturize: NSApp];
+
+
+ /* ZOOMED is not really maximized (actually it toggles between a user set size and
+ the program specified one), but comes closest since the default behavior is
+ "maximized" if the user did not intervene
+ */
+ if( pState->mnState == WINDOWSTATE_STATE_MAXIMIZED )
+ {
+ if(! [mpNSWindow isZoomed])
+ [mpNSWindow zoom: NSApp];
+ }
+ else
+ {
+ if( [mpNSWindow isZoomed] )
+ [mpNSWindow zoom: NSApp];
+ }
+ }
+
+ // get new geometry
+ UpdateFrameGeometry();
+
+ sal_uInt16 nEvent = 0;
+ if( pState->mnMask & (WINDOWSTATE_MASK_X | WINDOWSTATE_MASK_Y) )
+ {
+ mbPositioned = true;
+ nEvent = SALEVENT_MOVE;
+ }
+
+ if( pState->mnMask & (WINDOWSTATE_MASK_WIDTH | WINDOWSTATE_MASK_HEIGHT) )
+ {
+ mbSized = true;
+ nEvent = (nEvent == SALEVENT_MOVE) ? SALEVENT_MOVERESIZE : SALEVENT_RESIZE;
+ }
+ // send event that we were moved/sized
+ if( nEvent )
+ CallCallback( nEvent, NULL );
+
+ if( mbShown && mpNSWindow )
+ {
+ // trigger filling our backbuffer
+ SendPaintEvent();
+
+ // tell the system the views need to be updated
+ [mpNSWindow display];
+ }
+}
+
+// -----------------------------------------------------------------------
+
+sal_Bool AquaSalFrame::GetWindowState( SalFrameState* pState )
+{
+ if ( !mpNSWindow )
+ return FALSE;
+
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ pState->mnMask = WINDOWSTATE_MASK_X |
+ WINDOWSTATE_MASK_Y |
+ WINDOWSTATE_MASK_WIDTH |
+ WINDOWSTATE_MASK_HEIGHT |
+ WINDOWSTATE_MASK_STATE;
+
+ NSRect aStateRect = [mpNSWindow frame];
+ aStateRect = [NSWindow contentRectForFrameRect: aStateRect styleMask: mnStyleMask];
+ CocoaToVCL( aStateRect );
+ pState->mnX = long(aStateRect.origin.x);
+ pState->mnY = long(aStateRect.origin.y);
+ pState->mnWidth = long(aStateRect.size.width);
+ pState->mnHeight = long(aStateRect.size.height);
+
+ if( [mpNSWindow isMiniaturized] )
+ pState->mnState = WINDOWSTATE_STATE_MINIMIZED;
+ else if( ! [mpNSWindow isZoomed] )
+ pState->mnState = WINDOWSTATE_STATE_NORMAL;
+ else
+ pState->mnState = WINDOWSTATE_STATE_MAXIMIZED;
+
+ return TRUE;
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::SetScreenNumber(unsigned int nScreen)
+{
+ if ( !mpNSWindow )
+ return;
+
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ NSArray* pScreens = [NSScreen screens];
+ Rectangle aRet;
+ NSScreen* pScreen = nil;
+ if( pScreens && nScreen < [pScreens count] )
+ {
+ // get new screen frame
+ pScreen = [pScreens objectAtIndex: nScreen];
+ NSRect aNewScreen = [pScreen frame];
+
+ // get current screen frame
+ pScreen = [mpNSWindow screen];
+ if( pScreen )
+ {
+ NSRect aCurScreen = [pScreen frame];
+ if( aCurScreen.origin.x != aNewScreen.origin.x ||
+ aCurScreen.origin.y != aNewScreen.origin.y )
+ {
+ NSRect aFrameRect = [mpNSWindow frame];
+ aFrameRect.origin.x += aNewScreen.origin.x - aCurScreen.origin.x;
+ aFrameRect.origin.y += aNewScreen.origin.y - aCurScreen.origin.y;
+ [mpNSWindow setFrame: aFrameRect display: NO];
+ UpdateFrameGeometry();
+ }
+ }
+ }
+}
+
+void AquaSalFrame::SetApplicationID( const OUString &/*rApplicationID*/ )
+{
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::ShowFullScreen( sal_Bool bFullScreen, sal_Int32 nDisplay )
+{
+ if ( !mpNSWindow )
+ return;
+
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ SAL_INFO("vcl.macosx", OSL_THIS_FUNC << ": mbFullScreen=" << mbFullScreen << ", bFullScreen=" << bFullScreen);
+
+ if( mbFullScreen == bFullScreen )
+ return;
+
+ mbFullScreen = bFullScreen;
+
+ if( bFullScreen )
+ {
+ // hide the dock and the menubar if we are on the menu screen
+ // which is always on index 0 according to documentation
+ bool bHideMenu = (nDisplay == 0);
+
+ NSRect aNewContentRect = { { 0, 0 }, { 0, 0 } };
+ // get correct screen
+ NSScreen* pScreen = nil;
+ NSArray* pScreens = [NSScreen screens];
+ if( pScreens )
+ {
+ if( nDisplay >= 0 && (unsigned int)nDisplay < [pScreens count] )
+ pScreen = [pScreens objectAtIndex: nDisplay];
+ else
+ {
+ // this means span all screens
+ bHideMenu = true;
+ NSEnumerator* pEnum = [pScreens objectEnumerator];
+ while( (pScreen = [pEnum nextObject]) != nil )
+ {
+ NSRect aScreenRect = [pScreen frame];
+ if( aScreenRect.origin.x < aNewContentRect.origin.x )
+ {
+ aNewContentRect.size.width += aNewContentRect.origin.x - aScreenRect.origin.x;
+ aNewContentRect.origin.x = aScreenRect.origin.x;
+ }
+ if( aScreenRect.origin.y < aNewContentRect.origin.y )
+ {
+ aNewContentRect.size.height += aNewContentRect.origin.y - aScreenRect.origin.y;
+ aNewContentRect.origin.y = aScreenRect.origin.y;
+ }
+ if( aScreenRect.origin.x + aScreenRect.size.width > aNewContentRect.origin.x + aNewContentRect.size.width )
+ aNewContentRect.size.width = aScreenRect.origin.x + aScreenRect.size.width - aNewContentRect.origin.x;
+ if( aScreenRect.origin.y + aScreenRect.size.height > aNewContentRect.origin.y + aNewContentRect.size.height )
+ aNewContentRect.size.height = aScreenRect.origin.y + aScreenRect.size.height - aNewContentRect.origin.y;
+ }
+ }
+ }
+ if( aNewContentRect.size.width == 0 && aNewContentRect.size.height == 0 )
+ {
+ if( pScreen == nil )
+ pScreen = [mpNSWindow screen];
+ if( pScreen == nil )
+ pScreen = [NSScreen mainScreen];
+
+ aNewContentRect = [pScreen frame];
+ }
+
+ if( bHideMenu )
+ [NSMenu setMenuBarVisible:NO];
+
+ maFullScreenRect = [mpNSWindow frame];
+ {
+ [mpNSWindow setFrame: [NSWindow frameRectForContentRect: aNewContentRect styleMask: mnStyleMask] display: mbShown ? YES : NO];
+ }
+
+ UpdateFrameGeometry();
+
+ if( mbShown )
+ CallCallback( SALEVENT_MOVERESIZE, NULL );
+ }
+ else
+ {
+ {
+ [mpNSWindow setFrame: maFullScreenRect display: mbShown ? YES : NO];
+ }
+ UpdateFrameGeometry();
+
+ if( mbShown )
+ CallCallback( SALEVENT_MOVERESIZE, NULL );
+
+ // show the dock and the menubar
+ [NSMenu setMenuBarVisible:YES];
+ }
+ if( mbShown )
+ // trigger filling our backbuffer
+ SendPaintEvent();
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::StartPresentation( sal_Bool bStart )
+{
+ if ( !mpNSWindow )
+ return;
+
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ if( bStart )
+ {
+ GetSalData()->maPresentationFrames.push_back( this );
+ IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
+ kIOPMAssertionLevelOn,
+ CFSTR("LibreOffice presentation running"),
+ &mnAssertionID);
+ [mpNSWindow setLevel: NSPopUpMenuWindowLevel];
+ if( mbShown )
+ [mpNSWindow makeMainWindow];
+ }
+ else
+ {
+ GetSalData()->maPresentationFrames.remove( this );
+ IOPMAssertionRelease(mnAssertionID);
+ [mpNSWindow setLevel: NSNormalWindowLevel];
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::SetAlwaysOnTop( sal_Bool )
+{
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::ToTop(sal_uInt16 nFlags)
+{
+ if ( !mpNSWindow )
+ return;
+
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ if( ! (nFlags & SAL_FRAME_TOTOP_RESTOREWHENMIN) )
+ {
+ if( ! [mpNSWindow isVisible] || [mpNSWindow isMiniaturized] )
+ return;
+ }
+ if( nFlags & SAL_FRAME_TOTOP_GRABFOCUS )
+ [mpNSWindow makeKeyAndOrderFront: NSApp];
+ else
+ [mpNSWindow orderFront: NSApp];
+}
+
+// -----------------------------------------------------------------------
+
+NSCursor* AquaSalFrame::getCurrentCursor() const
+{
+ NSCursor* pCursor = nil;
+ switch( mePointerStyle )
+ {
+ case POINTER_TEXT: pCursor = [NSCursor IBeamCursor]; break;
+ case POINTER_CROSS: pCursor = [NSCursor crosshairCursor]; break;
+ case POINTER_HAND:
+ case POINTER_MOVE: pCursor = [NSCursor openHandCursor]; break;
+ case POINTER_NSIZE: pCursor = [NSCursor resizeUpCursor]; break;
+ case POINTER_SSIZE: pCursor = [NSCursor resizeDownCursor]; break;
+ case POINTER_ESIZE: pCursor = [NSCursor resizeRightCursor]; break;
+ case POINTER_WSIZE: pCursor = [NSCursor resizeLeftCursor]; break;
+ case POINTER_ARROW: pCursor = [NSCursor arrowCursor]; break;
+ case POINTER_VSPLIT:
+ case POINTER_VSIZEBAR:
+ case POINTER_WINDOW_NSIZE:
+ case POINTER_WINDOW_SSIZE:
+ pCursor = [NSCursor resizeUpDownCursor]; break;
+ case POINTER_HSPLIT:
+ case POINTER_HSIZEBAR:
+ case POINTER_WINDOW_ESIZE:
+ case POINTER_WINDOW_WSIZE:
+ pCursor = [NSCursor resizeLeftRightCursor]; break;
+ case POINTER_REFHAND: pCursor = [NSCursor pointingHandCursor]; break;
+ case POINTER_NULL: [NSCursor hide]; break;
+
+ default:
+ pCursor = GetSalData()->getCursor( mePointerStyle );
+ if( pCursor == nil )
+ {
+ OSL_FAIL( "unmapped cursor" );
+ pCursor = [NSCursor arrowCursor];
+ }
+ break;
+ }
+ return pCursor;
+}
+
+void AquaSalFrame::SetPointer( PointerStyle ePointerStyle )
+{
+ if ( !mpNSWindow )
+ return;
+
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ if( ePointerStyle >= POINTER_COUNT || ePointerStyle == mePointerStyle )
+ return;
+ mePointerStyle = ePointerStyle;
+
+ [mpNSWindow invalidateCursorRectsForView: mpNSView];
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::SetPointerPos( long nX, long nY )
+{
+ // FIXME: use Cocoa functions
+
+ // FIXME: multiscreen support
+ CGPoint aPoint = { static_cast<CGFloat>(nX + maGeometry.nX), static_cast<CGFloat>(nY + maGeometry.nY) };
+ CGDirectDisplayID mainDisplayID = CGMainDisplayID();
+ CGDisplayMoveCursorToPoint( mainDisplayID, aPoint );
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::Flush( void )
+{
+ if( !(mbGraphics && mpGraphics && mpNSView && mbShown) )
+ return;
+
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+
+ [mpNSView setNeedsDisplay: YES];
+
+ // outside of the application's event loop (e.g. IntroWindow)
+ // nothing would trigger paint event handling
+ // => fall back to synchronous painting
+ if( ImplGetSVData()->maAppData.mnDispatchLevel <= 0 )
+ {
+ [mpNSView display];
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::Flush( const Rectangle& rRect )
+{
+ if( !(mbGraphics && mpGraphics && mpNSView && mbShown) )
+ return;
+
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ NSRect aNSRect = { { static_cast<CGFloat>(rRect.Left()), static_cast<CGFloat>(rRect.Top()) }, { static_cast<CGFloat>(rRect.GetWidth()), static_cast<CGFloat>(rRect.GetHeight()) } };
+ VCLToCocoa( aNSRect, false );
+ [mpNSView setNeedsDisplayInRect: aNSRect];
+
+ // outside of the application's event loop (e.g. IntroWindow)
+ // nothing would trigger paint event handling
+ // => fall back to synchronous painting
+ if( ImplGetSVData()->maAppData.mnDispatchLevel <= 0 )
+ {
+ [mpNSView display];
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::Sync()
+{
+ if( mbGraphics && mpGraphics && mpNSView && mbShown )
+ {
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ [mpNSView setNeedsDisplay: YES];
+ [mpNSView display];
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::SetInputContext( SalInputContext* pContext )
+{
+ if (!pContext)
+ {
+ mnICOptions = 0;
+ return;
+ }
+
+ mnICOptions = pContext->mnOptions;
+
+ if(!(pContext->mnOptions & SAL_INPUTCONTEXT_TEXT))
+ return;
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::EndExtTextInput( sal_uInt16 )
+{
+}
+
+// -----------------------------------------------------------------------
+
+OUString AquaSalFrame::GetKeyName( sal_uInt16 nKeyCode )
+{
+ static std::map< sal_uInt16, OUString > aKeyMap;
+ if( aKeyMap.empty() )
+ {
+ sal_uInt16 i;
+ for( i = KEY_A; i <= KEY_Z; i++ )
+ aKeyMap[ i ] = OUString( sal_Unicode( 'A' + (i - KEY_A) ) );
+ for( i = KEY_0; i <= KEY_9; i++ )
+ aKeyMap[ i ] = OUString( sal_Unicode( '0' + (i - KEY_0) ) );
+ for( i = KEY_F1; i <= KEY_F26; i++ )
+ {
+ OUStringBuffer aKey( 3 );
+ aKey.append( 'F' );
+ aKey.append( sal_Int32( i - KEY_F1 + 1 ) );
+ aKeyMap[ i ] = aKey.makeStringAndClear();
+ }
+
+ aKeyMap[ KEY_DOWN ] = OUString( sal_Unicode( 0x21e3 ) );
+ aKeyMap[ KEY_UP ] = OUString( sal_Unicode( 0x21e1 ) );
+ aKeyMap[ KEY_LEFT ] = OUString( sal_Unicode( 0x21e0 ) );
+ aKeyMap[ KEY_RIGHT ] = OUString( sal_Unicode( 0x21e2 ) );
+ aKeyMap[ KEY_HOME ] = OUString( sal_Unicode( 0x2196 ) );
+ aKeyMap[ KEY_END ] = OUString( sal_Unicode( 0x2198 ) );
+ aKeyMap[ KEY_PAGEUP ] = OUString( sal_Unicode( 0x21de ) );
+ aKeyMap[ KEY_PAGEDOWN ] = OUString( sal_Unicode( 0x21df ) );
+ aKeyMap[ KEY_RETURN ] = OUString( sal_Unicode( 0x21a9 ) );
+ aKeyMap[ KEY_ESCAPE ] = OUString( "esc" );
+ aKeyMap[ KEY_TAB ] = OUString( sal_Unicode( 0x21e5 ) );
+ aKeyMap[ KEY_BACKSPACE ]= OUString( sal_Unicode( 0x232b ) );
+ aKeyMap[ KEY_SPACE ] = OUString( sal_Unicode( 0x2423 ) );
+ aKeyMap[ KEY_DELETE ] = OUString( sal_Unicode( 0x2326 ) );
+ aKeyMap[ KEY_ADD ] = OUString( '+' );
+ aKeyMap[ KEY_SUBTRACT ] = OUString( '-' );
+ aKeyMap[ KEY_DIVIDE ] = OUString( '/' );
+ aKeyMap[ KEY_MULTIPLY ] = OUString( '*' );
+ aKeyMap[ KEY_POINT ] = OUString( '.' );
+ aKeyMap[ KEY_COMMA ] = OUString( ',' );
+ aKeyMap[ KEY_LESS ] = OUString( '<' );
+ aKeyMap[ KEY_GREATER ] = OUString( '>' );
+ aKeyMap[ KEY_EQUAL ] = OUString( '=' );
+ aKeyMap[ KEY_OPEN ] = OUString( sal_Unicode( 0x23cf ) );
+
+ /* yet unmapped KEYCODES:
+ aKeyMap[ KEY_INSERT ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_CUT ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_COPY ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_PASTE ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_UNDO ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_REPEAT ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_FIND ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_PROPERTIES ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_FRONT ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_CONTEXTMENU ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_MENU ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_HELP ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_HANGUL_HANJA ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_DECIMAL ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_TILDE ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_QUOTELEFT ]= OUString( sal_Unicode( ) );
+ */
+
+ }
+
+ OUStringBuffer aResult( 16 );
+
+ sal_uInt16 nUnmodifiedCode = (nKeyCode & KEY_CODE);
+ std::map< sal_uInt16, OUString >::const_iterator it = aKeyMap.find( nUnmodifiedCode );
+ if( it != aKeyMap.end() )
+ {
+ if( (nKeyCode & KEY_SHIFT) != 0 )
+ aResult.append( sal_Unicode( 0x21e7 ) );
+ if( (nKeyCode & KEY_MOD1) != 0 )
+ aResult.append( sal_Unicode( 0x2318 ) );
+ // we do not really handle Alt (see below)
+ // we map it to MOD3, whichis actually Command
+ if( (nKeyCode & (KEY_MOD2|KEY_MOD3)) != 0 )
+ aResult.append( sal_Unicode( 0x2303 ) );
+
+ aResult.append( it->second );
+ }
+
+ return aResult.makeStringAndClear();
+}
+
+// -----------------------------------------------------------------------
+
+static void getAppleScrollBarVariant(StyleSettings &rSettings)
+{
+ bool bIsScrollbarDoubleMax = true; // default is DoubleMax
+
+ CFStringRef AppleScrollBarType = CFSTR("AppleScrollBarVariant");
+ if( AppleScrollBarType )
+ {
+ CFStringRef ScrollBarVariant = ((CFStringRef)CFPreferencesCopyAppValue( AppleScrollBarType, kCFPreferencesCurrentApplication ));
+ if( ScrollBarVariant )
+ {
+ if( CFGetTypeID( ScrollBarVariant ) == CFStringGetTypeID() )
+ {
+ // TODO: check for the less important variants "DoubleMin" and "DoubleBoth" too
+ CFStringRef DoubleMax = CFSTR("DoubleMax");
+ if (DoubleMax)
+ {
+ if ( !CFStringCompare(ScrollBarVariant, DoubleMax, kCFCompareCaseInsensitive) )
+ bIsScrollbarDoubleMax = true;
+ else
+ bIsScrollbarDoubleMax = false;
+ CFRelease(DoubleMax);
+ }
+ }
+ CFRelease( ScrollBarVariant );
+ }
+ CFRelease(AppleScrollBarType);
+ }
+
+ GetSalData()->mbIsScrollbarDoubleMax = bIsScrollbarDoubleMax;
+
+ CFStringRef jumpScroll = CFSTR("AppleScrollerPagingBehavior");
+ if( jumpScroll )
+ {
+ CFBooleanRef jumpStr = ((CFBooleanRef)CFPreferencesCopyAppValue( jumpScroll, kCFPreferencesCurrentApplication ));
+ if( jumpStr )
+ {
+ if( CFGetTypeID( jumpStr ) == CFBooleanGetTypeID() )
+ rSettings.SetPrimaryButtonWarpsSlider(jumpStr == kCFBooleanTrue);
+ CFRelease( jumpStr );
+ }
+ CFRelease( jumpScroll );
+ }
+}
+
+static Color getColor( NSColor* pSysColor, const Color& rDefault, NSWindow* pWin )
+{
+ Color aRet( rDefault );
+ if( pSysColor )
+ {
+ // transform to RGB
+ NSColor* pRBGColor = [pSysColor colorUsingColorSpaceName: NSDeviceRGBColorSpace device: [pWin deviceDescription]];
+ if( pRBGColor )
+ {
+ CGFloat r = 0, g = 0, b = 0, a = 0;
+ [pRBGColor getRed: &r green: &g blue: &b alpha: &a];
+ aRet = Color( int(r*255.999), int(g*255.999), int(b*255.999) );
+ /*
+ do not release here; leads to duplicate free in yield
+ it seems the converted color comes out autoreleased, although this
+ is not documented
+ [pRBGColor release];
+ */
+ }
+ }
+ return aRet;
+}
+
+static Font getFont( NSFont* pFont, long nDPIY, const Font& rDefault )
+{
+ Font aResult( rDefault );
+ if( pFont )
+ {
+ aResult.SetName( GetOUString( [pFont familyName] ) );
+ aResult.SetHeight( static_cast<int>(([pFont pointSize] * 72.0 / (float)nDPIY)+0.5) );
+ aResult.SetItalic( ([pFont italicAngle] != 0.0) ? ITALIC_NORMAL : ITALIC_NONE );
+ // FIMXE: bold ?
+ }
+
+ return aResult;
+}
+
+void AquaSalFrame::getResolution( sal_Int32& o_rDPIX, sal_Int32& o_rDPIY )
+{
+ if( ! mpGraphics )
+ {
+ GetGraphics();
+ ReleaseGraphics( mpGraphics );
+ }
+ mpGraphics->GetResolution( o_rDPIX, o_rDPIY );
+}
+
+// on OSX-Aqua the style settings are independent of the frame, so it does
+// not really belong here. Since the connection to the Appearance_Manager
+// is currently done in salnativewidgets.cxx this would be a good place.
+// On the other hand VCL's platform independent code currently only asks
+// SalFrames for system settings anyway, so moving the code somewhere else
+// doesn't make the anything cleaner for now
+void AquaSalFrame::UpdateSettings( AllSettings& rSettings )
+{
+ if ( !mpNSWindow )
+ return;
+
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ [mpNSView lockFocus];
+
+ StyleSettings aStyleSettings = rSettings.GetStyleSettings();
+
+ // Background Color
+ Color aBackgroundColor = Color( 0xEC, 0xEC, 0xEC );
+ aStyleSettings.Set3DColors( aBackgroundColor );
+ aStyleSettings.SetFaceColor( aBackgroundColor );
+ Color aInactiveTabColor( aBackgroundColor );
+ aInactiveTabColor.DecreaseLuminance( 32 );
+ aStyleSettings.SetInactiveTabColor( aInactiveTabColor );
+
+ aStyleSettings.SetDialogColor( aBackgroundColor );
+ aStyleSettings.SetLightBorderColor( aBackgroundColor );
+ Color aShadowColor( aStyleSettings.GetShadowColor() );
+ aShadowColor.IncreaseLuminance( 32 );
+ aStyleSettings.SetShadowColor( aShadowColor );
+
+ // get the system font settings
+ Font aAppFont = aStyleSettings.GetAppFont();
+ sal_Int32 nDPIX = 72, nDPIY = 72;
+ getResolution( nDPIX, nDPIY );
+ aAppFont = getFont( [NSFont systemFontOfSize: 0], nDPIY, aAppFont );
+
+ aStyleSettings.SetToolbarIconSize( STYLE_TOOLBAR_ICONSIZE_LARGE );
+
+ // TODO: better mapping of OS X<->LibreOffice font settings
+ aStyleSettings.SetAppFont( aAppFont );
+ aStyleSettings.SetHelpFont( aAppFont );
+ aStyleSettings.SetPushButtonFont( aAppFont );
+
+ Font aTitleFont( getFont( [NSFont titleBarFontOfSize: 0], nDPIY, aAppFont ) );
+ aStyleSettings.SetTitleFont( aTitleFont );
+ aStyleSettings.SetFloatTitleFont( aTitleFont );
+
+ Font aMenuFont( getFont( [NSFont menuFontOfSize: 0], nDPIY, aAppFont ) );
+ aStyleSettings.SetMenuFont( aMenuFont );
+
+ aStyleSettings.SetToolFont( aAppFont );
+
+ Font aLabelFont( getFont( [NSFont labelFontOfSize: 0], nDPIY, aAppFont ) );
+ aStyleSettings.SetLabelFont( aLabelFont );
+ aStyleSettings.SetInfoFont( aLabelFont );
+ aStyleSettings.SetRadioCheckFont( aLabelFont );
+ aStyleSettings.SetFieldFont( aLabelFont );
+ aStyleSettings.SetGroupFont( aLabelFont );
+ aStyleSettings.SetIconFont( aLabelFont );
+
+ Color aHighlightColor( getColor( [NSColor selectedTextBackgroundColor],
+ aStyleSettings.GetHighlightColor(), mpNSWindow ) );
+ aStyleSettings.SetHighlightColor( aHighlightColor );
+ Color aHighlightTextColor( getColor( [NSColor selectedTextColor],
+ aStyleSettings.GetHighlightTextColor(), mpNSWindow ) );
+ aStyleSettings.SetHighlightTextColor( aHighlightTextColor );
+
+ Color aMenuHighlightColor( getColor( [NSColor selectedMenuItemColor],
+ aStyleSettings.GetMenuHighlightColor(), mpNSWindow ) );
+ aStyleSettings.SetMenuHighlightColor( aMenuHighlightColor );
+ Color aMenuHighlightTextColor( getColor( [NSColor selectedMenuItemTextColor],
+ aStyleSettings.GetMenuHighlightTextColor(), mpNSWindow ) );
+ aStyleSettings.SetMenuHighlightTextColor( aMenuHighlightTextColor );
+
+ aStyleSettings.SetMenuColor( aBackgroundColor );
+ Color aMenuTextColor( getColor( [NSColor textColor],
+ aStyleSettings.GetMenuTextColor(), mpNSWindow ) );
+ aStyleSettings.SetMenuTextColor( aMenuTextColor );
+ aStyleSettings.SetMenuBarTextColor( aMenuTextColor );
+ aStyleSettings.SetMenuBarRolloverTextColor( aMenuTextColor );
+
+ aStyleSettings.SetCursorBlinkTime( 500 );
+
+ // no mnemonics on OS X
+ aStyleSettings.SetOptions( aStyleSettings.GetOptions() | STYLE_OPTION_NOMNEMONICS );
+
+ getAppleScrollBarVariant(aStyleSettings);
+
+ // set scrollbar size
+ aStyleSettings.SetScrollBarSize( static_cast<long int>([NSScroller scrollerWidth]) );
+
+ // images in menus false for MacOSX
+ aStyleSettings.SetPreferredUseImagesInMenus( false );
+ aStyleSettings.SetHideDisabledMenuItems( sal_True );
+ aStyleSettings.SetAcceleratorsInContextMenus( sal_False );
+
+ rSettings.SetStyleSettings( aStyleSettings );
+
+ [mpNSView unlockFocus];
+}
+
+// -----------------------------------------------------------------------
+
+const SystemEnvData* AquaSalFrame::GetSystemData() const
+{
+ return &maSysData;
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::Beep()
+{
+ NSBeep();
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::SetPosSize(long nX, long nY, long nWidth, long nHeight, sal_uInt16 nFlags)
+{
+ if ( !mpNSWindow )
+ return;
+
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ sal_uInt16 nEvent = 0;
+
+ if( [mpNSWindow isMiniaturized] )
+ [mpNSWindow deminiaturize: NSApp]; // expand the window
+
+ if (nFlags & (SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y))
+ {
+ mbPositioned = true;
+ nEvent = SALEVENT_MOVE;
+ }
+
+ if (nFlags & (SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT))
+ {
+ mbSized = true;
+ nEvent = (nEvent == SALEVENT_MOVE) ? SALEVENT_MOVERESIZE : SALEVENT_RESIZE;
+ }
+
+ NSRect aFrameRect = [mpNSWindow frame];
+ NSRect aContentRect = [NSWindow contentRectForFrameRect: aFrameRect styleMask: mnStyleMask];
+
+ // position is always relative to parent frame
+ NSRect aParentContentRect;
+
+ if( mpParent )
+ {
+ if( Application::GetSettings().GetLayoutRTL() )
+ {
+ if( (nFlags & SAL_FRAME_POSSIZE_WIDTH) != 0 )
+ nX = mpParent->maGeometry.nWidth - nWidth-1 - nX;
+ else
+ nX = mpParent->maGeometry.nWidth - static_cast<long int>( aContentRect.size.width-1) - nX;
+ }
+ NSRect aParentFrameRect = [mpParent->mpNSWindow frame];
+ aParentContentRect = [NSWindow contentRectForFrameRect: aParentFrameRect styleMask: mpParent->mnStyleMask];
+ }
+ else
+ aParentContentRect = maScreenRect; // use screen if no parent
+
+ CocoaToVCL( aContentRect );
+ CocoaToVCL( aParentContentRect );
+
+ bool bPaint = false;
+ if( (nFlags & (SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT)) != 0 )
+ {
+ if( nWidth != aContentRect.size.width || nHeight != aContentRect.size.height )
+ bPaint = true;
+ }
+
+ // use old window pos if no new pos requested
+ if( (nFlags & SAL_FRAME_POSSIZE_X) != 0 )
+ aContentRect.origin.x = nX + aParentContentRect.origin.x;
+ if( (nFlags & SAL_FRAME_POSSIZE_Y) != 0)
+ aContentRect.origin.y = nY + aParentContentRect.origin.y;
+
+ // use old size if no new size requested
+ if( (nFlags & SAL_FRAME_POSSIZE_WIDTH) != 0 )
+ aContentRect.size.width = nWidth;
+ if( (nFlags & SAL_FRAME_POSSIZE_HEIGHT) != 0)
+ aContentRect.size.height = nHeight;
+
+ VCLToCocoa( aContentRect );
+
+ // do not display yet, we need to update our backbuffer
+ {
+ [mpNSWindow setFrame: [NSWindow frameRectForContentRect: aContentRect styleMask: mnStyleMask] display: NO];
+ }
+
+ UpdateFrameGeometry();
+
+ if (nEvent)
+ CallCallback(nEvent, NULL);
+
+ if( mbShown && bPaint )
+ {
+ // trigger filling our backbuffer
+ SendPaintEvent();
+
+ // now inform the system that the views need to be drawn
+ [mpNSWindow display];
+ }
+}
+
+void AquaSalFrame::GetWorkArea( Rectangle& rRect )
+{
+ if ( !mpNSWindow )
+ return;
+
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ NSScreen* pScreen = [mpNSWindow screen];
+ if( pScreen == nil )
+ pScreen = [NSScreen mainScreen];
+ NSRect aRect = [pScreen visibleFrame];
+ CocoaToVCL( aRect );
+ rRect.Left() = static_cast<long>(aRect.origin.x);
+ rRect.Top() = static_cast<long>(aRect.origin.y);
+ rRect.Right() = static_cast<long>(aRect.origin.x + aRect.size.width - 1);
+ rRect.Bottom() = static_cast<long>(aRect.origin.y + aRect.size.height - 1);
+}
+
+SalPointerState AquaSalFrame::GetPointerState()
+{
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ SalPointerState state;
+ state.mnState = 0;
+
+ // get position
+ NSPoint aPt = [mpNSWindow mouseLocationOutsideOfEventStream];
+ CocoaToVCL( aPt, false );
+ state.maPos = Point(static_cast<long>(aPt.x), static_cast<long>(aPt.y));
+
+ NSEvent* pCur = [NSApp currentEvent];
+ bool bMouseEvent = false;
+ if( pCur )
+ {
+ bMouseEvent = true;
+ switch( [pCur type] )
+ {
+ case NSLeftMouseDown: state.mnState |= MOUSE_LEFT; break;
+ case NSLeftMouseUp: break;
+ case NSRightMouseDown: state.mnState |= MOUSE_RIGHT; break;
+ case NSRightMouseUp: break;
+ case NSOtherMouseDown: state.mnState |= ([pCur buttonNumber] == 2) ? MOUSE_MIDDLE : 0; break;
+ case NSOtherMouseUp: break;
+ case NSMouseMoved: break;
+ case NSLeftMouseDragged: state.mnState |= MOUSE_LEFT; break;
+ case NSRightMouseDragged: state.mnState |= MOUSE_RIGHT; break;
+ case NSOtherMouseDragged: state.mnState |= ([pCur buttonNumber] == 2) ? MOUSE_MIDDLE : 0; break;
+ break;
+ default:
+ bMouseEvent = false;
+ break;
+ }
+ }
+ if( bMouseEvent )
+ {
+ unsigned int nMask = (unsigned int)[pCur modifierFlags];
+ if( (nMask & NSShiftKeyMask) != 0 )
+ state.mnState |= KEY_SHIFT;
+ if( (nMask & NSControlKeyMask) != 0 )
+ state.mnState |= KEY_MOD3;
+ if( (nMask & NSAlternateKeyMask) != 0 )
+ state.mnState |= KEY_MOD2;
+ if( (nMask & NSCommandKeyMask) != 0 )
+ state.mnState |= KEY_MOD1;
+
+ }
+ else
+ {
+ // FIXME: replace Carbon by Cocoa
+ // Cocoa does not have an equivalent for GetCurrentEventButtonState
+ // and GetCurrentEventKeyModifiers.
+ // we could try to get away with tracking all events for modifierKeys
+ // and all mouse events for button state in VCL_NSApllication::sendEvent,
+ // but it is unclear whether this will get us the same result.
+ // leave in GetCurrentEventButtonState and GetCurrentEventKeyModifiers for now
+
+ // fill in button state
+ UInt32 nState = GetCurrentEventButtonState();
+ state.mnState = 0;
+ if( nState & 1 )
+ state.mnState |= MOUSE_LEFT; // primary button
+ if( nState & 2 )
+ state.mnState |= MOUSE_RIGHT; // secondary button
+ if( nState & 4 )
+ state.mnState |= MOUSE_MIDDLE; // tertiary button
+
+ // fill in modifier state
+ nState = GetCurrentEventKeyModifiers();
+ if( nState & shiftKey )
+ state.mnState |= KEY_SHIFT;
+ if( nState & controlKey )
+ state.mnState |= KEY_MOD3;
+ if( nState & optionKey )
+ state.mnState |= KEY_MOD2;
+ if( nState & cmdKey )
+ state.mnState |= KEY_MOD1;
+ }
+
+
+ return state;
+}
+
+SalFrame::SalIndicatorState AquaSalFrame::GetIndicatorState()
+{
+ SalIndicatorState aState;
+ aState.mnState = 0;
+ return aState;
+}
+
+void AquaSalFrame::SimulateKeyPress( sal_uInt16 /*nKeyCode*/ )
+{
+}
+
+bool AquaSalFrame::SetPluginParent( SystemParentData* )
+{
+ // plugin parent may be killed unexpectedly by
+ // plugging process;
+
+ //TODO: implement
+ return sal_False;
+}
+
+sal_Bool AquaSalFrame::MapUnicodeToKeyCode( sal_Unicode , LanguageType , KeyCode& )
+{
+ // not supported yet
+ return FALSE;
+}
+
+LanguageType AquaSalFrame::GetInputLanguage()
+{
+ //TODO: implement
+ return LANGUAGE_DONTKNOW;
+}
+
+void AquaSalFrame::DrawMenuBar()
+{
+}
+
+void AquaSalFrame::SetMenu( SalMenu* pSalMenu )
+{
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ AquaSalMenu* pMenu = static_cast<AquaSalMenu*>(pSalMenu);
+ DBG_ASSERT( ! pMenu || pMenu->mbMenuBar, "setting non menubar on frame" );
+ mpMenu = pMenu;
+ if( mpMenu )
+ mpMenu->setMainMenu();
+}
+
+void AquaSalFrame::SetExtendedFrameStyle( SalExtStyle nStyle )
+{
+ if ( mpNSWindow )
+ {
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ if( (mnExtStyle & SAL_FRAME_EXT_STYLE_DOCMODIFIED) != (nStyle & SAL_FRAME_EXT_STYLE_DOCMODIFIED) )
+ [mpNSWindow setDocumentEdited: (nStyle & SAL_FRAME_EXT_STYLE_DOCMODIFIED) ? YES : NO];
+ }
+
+ mnExtStyle = nStyle;
+}
+
+SalFrame* AquaSalFrame::GetParent() const
+{
+ return mpParent;
+}
+
+void AquaSalFrame::SetParent( SalFrame* pNewParent )
+{
+ bool bShown = mbShown;
+ // remove from child list
+ Show( FALSE );
+ mpParent = (AquaSalFrame*)pNewParent;
+ // insert to correct parent and paint
+ Show( bShown );
+}
+
+void AquaSalFrame::UpdateFrameGeometry()
+{
+ if ( !mpNSWindow )
+ {
+ return;
+ }
+
+ // keep in mind that view and window coordinates are lower left
+ // whereas vcl's are upper left
+
+ // update screen rect
+ NSScreen * pScreen = [mpNSWindow screen];
+ if( pScreen )
+ {
+ maScreenRect = [pScreen frame];
+ NSArray* pScreens = [NSScreen screens];
+ if( pScreens )
+ maGeometry.nDisplayScreenNumber = [pScreens indexOfObject: pScreen];
+ }
+
+ NSRect aFrameRect = [mpNSWindow frame];
+ NSRect aContentRect = [NSWindow contentRectForFrameRect: aFrameRect styleMask: mnStyleMask];
+
+ // release old track rect
+ [mpNSView removeTrackingRect: mnTrackingRectTag];
+ // install the new track rect
+ NSRect aTrackRect = { { 0, 0 }, aContentRect.size };
+ mnTrackingRectTag = [mpNSView addTrackingRect: aTrackRect owner: mpNSView userData: nil assumeInside: NO];
+
+ // convert to vcl convention
+ CocoaToVCL( aFrameRect );
+ CocoaToVCL( aContentRect );
+
+ maGeometry.nX = static_cast<int>(aContentRect.origin.x);
+ maGeometry.nY = static_cast<int>(aContentRect.origin.y);
+
+ maGeometry.nLeftDecoration = static_cast<unsigned int>(aContentRect.origin.x - aFrameRect.origin.x);
+ maGeometry.nRightDecoration = static_cast<unsigned int>((aFrameRect.origin.x + aFrameRect.size.width) -
+ (aContentRect.origin.x + aContentRect.size.width));
+
+ maGeometry.nTopDecoration = static_cast<unsigned int>(aContentRect.origin.y - aFrameRect.origin.y);
+ maGeometry.nBottomDecoration = static_cast<unsigned int>((aFrameRect.origin.y + aFrameRect.size.height) -
+ (aContentRect.origin.y + aContentRect.size.height));
+
+ maGeometry.nWidth = static_cast<unsigned int>(aContentRect.size.width);
+ maGeometry.nHeight = static_cast<unsigned int>(aContentRect.size.height);
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalFrame::CaptureMouse( sal_Bool bCapture )
+{
+ /* Remark:
+ we'll try to use a pidgin version of capture mouse
+ on MacOSX (neither carbon nor cocoa) there is a
+ CaptureMouse equivalent (in Carbon there is TrackMouseLocation
+ but this is useless to use since it is blocking)
+
+ However on cocoa the active frame seems to get mouse events
+ also outside the window, so we'll try to forward mouse events
+ to the capture frame in the hope that one of our frames
+ gets a mouse event.
+
+ This will break as soon as the user activates another app, but
+ a mouse click will normally lead to a release of the mouse anyway.
+
+ Let's see how far we get this way. Alternatively we could use one
+ large overlay window like we did for the carbon implementation,
+ however that is resource intensive.
+ */
+
+ if( bCapture )
+ s_pCaptureFrame = this;
+ else if( ! bCapture && s_pCaptureFrame == this )
+ s_pCaptureFrame = NULL;
+}
+
+void AquaSalFrame::ResetClipRegion()
+{
+ if ( !mpNSWindow )
+ {
+ return;
+ }
+
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ // release old path and indicate no clipping
+ CGPathRelease( mrClippingPath );
+ mrClippingPath = NULL;
+
+ if( mpNSView && mbShown )
+ [mpNSView setNeedsDisplay: YES];
+ if( mpNSWindow )
+ {
+ [mpNSWindow setOpaque: YES];
+ [mpNSWindow invalidateShadow];
+ }
+}
+
+void AquaSalFrame::BeginSetClipRegion( sal_uLong nRects )
+{
+ if ( !mpNSWindow )
+ {
+ return;
+ }
+
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ // release old path
+ if( mrClippingPath )
+ {
+ CGPathRelease( mrClippingPath );
+ mrClippingPath = NULL;
+ }
+
+ if( maClippingRects.size() > SAL_CLIPRECT_COUNT && nRects < maClippingRects.size() )
+ {
+ std::vector<CGRect> aEmptyVec;
+ maClippingRects.swap( aEmptyVec );
+ }
+ maClippingRects.clear();
+ maClippingRects.reserve( nRects );
+}
+
+void AquaSalFrame::UnionClipRegion( long nX, long nY, long nWidth, long nHeight )
+{
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ if( nWidth && nHeight )
+ {
+ NSRect aRect = { { static_cast<CGFloat>(nX), static_cast<CGFloat>(nY) }, { static_cast<CGFloat>(nWidth), static_cast<CGFloat>(nHeight) } };
+ VCLToCocoa( aRect, false );
+ maClippingRects.push_back( CGRectMake(aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height) );
+ }
+}
+
+void AquaSalFrame::EndSetClipRegion()
+{
+ if ( !mpNSWindow )
+ {
+ return;
+ }
+
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ if( ! maClippingRects.empty() )
+ {
+ mrClippingPath = CGPathCreateMutable();
+ CGPathAddRects( mrClippingPath, NULL, &maClippingRects[0], maClippingRects.size() );
+ }
+ if( mpNSView && mbShown )
+ [mpNSView setNeedsDisplay: YES];
+ if( mpNSWindow )
+ {
+ [mpNSWindow setOpaque: (mrClippingPath != NULL) ? NO : YES];
+ [mpNSWindow setBackgroundColor: [NSColor clearColor]];
+ // shadow is invalidated when view gets drawn again
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/salframeview.mm b/vcl/osx/salframeview.mm
new file mode 100644
index 000000000000..81ae1cdce8c3
--- /dev/null
+++ b/vcl/osx/salframeview.mm
@@ -0,0 +1,1805 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/alloca.h>
+#include <sal/macros.h>
+
+#include "tools/helpers.hxx"
+#include "vcl/window.hxx"
+#include "vcl/svapp.hxx"
+
+#include "osx/salinst.h"
+#include "quartz/salgdi.h"
+#include "osx/salframe.h"
+#include "osx/salframeview.h"
+#include "osx/a11yfactory.h"
+#include "quartz/utils.h"
+
+#define WHEEL_EVENT_FACTOR 1.5
+
+// for allowing fullscreen support on deployment targets < OSX 10.7
+#if !defined(MAC_OS_X_VERSION_10_7)
+ #define NSWindowCollectionBehaviorFullScreenPrimary (1 << 7)
+ #define NSWindowCollectionBehaviorFullScreenAuxiliary (1 << 8)
+// #define NSFullScreenWindowMask (1 << 14)
+#endif
+
+
+static sal_uInt16 ImplGetModifierMask( unsigned int nMask )
+{
+ sal_uInt16 nRet = 0;
+ if( (nMask & NSShiftKeyMask) != 0 )
+ nRet |= KEY_SHIFT;
+ if( (nMask & NSControlKeyMask) != 0 )
+ nRet |= KEY_MOD3;
+ if( (nMask & NSAlternateKeyMask) != 0 )
+ nRet |= KEY_MOD2;
+ if( (nMask & NSCommandKeyMask) != 0 )
+ nRet |= KEY_MOD1;
+ return nRet;
+}
+
+static sal_uInt16 ImplMapCharCode( sal_Unicode aCode )
+{
+ static sal_uInt16 aKeyCodeMap[ 128 ] =
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ KEY_BACKSPACE, KEY_TAB, KEY_RETURN, 0, 0, KEY_RETURN, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, KEY_TAB, 0, KEY_ESCAPE, 0, 0, 0, 0,
+ KEY_SPACE, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, KEY_MULTIPLY, KEY_ADD, KEY_COMMA, KEY_SUBTRACT, KEY_POINT, KEY_DIVIDE,
+ KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7,
+ KEY_8, KEY_9, 0, 0, KEY_LESS, KEY_EQUAL, KEY_GREATER, 0,
+ 0, KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G,
+ KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O,
+ KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W,
+ KEY_X, KEY_Y, KEY_Z, 0, 0, 0, 0, 0,
+ KEY_QUOTELEFT, KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G,
+ KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O,
+ KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W,
+ KEY_X, KEY_Y, KEY_Z, 0, 0, 0, KEY_TILDE, KEY_BACKSPACE
+ };
+
+ // Note: the mapping 0x7f should by rights be KEY_DELETE
+ // however if you press "backspace" 0x7f is reported
+ // whereas for "delete" 0xf728 gets reported
+
+ // Note: the mapping of 0x19 to KEY_TAB is because for unknown reasons
+ // tab alone is reported as 0x09 (as expected) but shift-tab is
+ // reported as 0x19 (end of medium)
+
+ static sal_uInt16 aFunctionKeyCodeMap[ 128 ] =
+ {
+ KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_F1, KEY_F2, KEY_F3, KEY_F4,
+ KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12,
+ KEY_F13, KEY_F14, KEY_F15, KEY_F16, KEY_F17, KEY_F18, KEY_F19, KEY_F20,
+ KEY_F21, KEY_F22, KEY_F23, KEY_F24, KEY_F25, KEY_F26, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, KEY_INSERT,
+ KEY_DELETE, KEY_HOME, 0, KEY_END, KEY_PAGEUP, KEY_PAGEDOWN, 0, 0,
+ 0, 0, 0, 0, 0, KEY_MENU, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, KEY_UNDO, KEY_REPEAT, KEY_FIND, KEY_HELP, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+ };
+
+ sal_uInt16 nKeyCode = 0;
+ if( aCode < SAL_N_ELEMENTS( aKeyCodeMap) )
+ nKeyCode = aKeyCodeMap[ aCode ];
+ else if( aCode >= 0xf700 && aCode < 0xf780 )
+ nKeyCode = aFunctionKeyCodeMap[ aCode - 0xf700 ];
+ return nKeyCode;
+}
+
+static sal_uInt16 ImplMapKeyCode(sal_uInt16 nKeyCode)
+{
+ /*
+ http://stackoverflow.com/questions/2080312/where-can-i-find-a-list-of-key-codes-for-use-with-cocoas-nsevent-class/2080324#2080324
+ /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h
+ */
+
+ static sal_uInt16 aKeyCodeMap[ 0x80 ] =
+ {
+ KEY_A, KEY_S, KEY_D, KEY_F, KEY_H, KEY_G, KEY_Z, KEY_X,
+ KEY_C, KEY_V, 0, KEY_B, KEY_Q, KEY_W, KEY_E, KEY_R,
+ KEY_Y, KEY_T, KEY_1, KEY_2, KEY_3, KEY_4, KEY_6, KEY_5,
+ KEY_EQUAL, KEY_9, KEY_7, KEY_SUBTRACT, KEY_8, KEY_0, KEY_BRACKETRIGHT, KEY_0,
+ KEY_U, KEY_BRACKETLEFT, KEY_I, KEY_P, KEY_RETURN, KEY_L, KEY_J, KEY_QUOTELEFT,
+ KEY_K, KEY_SEMICOLON, 0, KEY_COMMA, KEY_DIVIDE, KEY_N, KEY_M, KEY_POINT,
+ KEY_TAB, KEY_SPACE, KEY_TILDE, KEY_DELETE, 0, KEY_ESCAPE, 0, 0,
+ 0, KEY_CAPSLOCK, 0, 0, 0, 0, 0, 0,
+ KEY_F17, KEY_DECIMAL, 0, KEY_MULTIPLY, 0, KEY_ADD, 0, 0,
+ 0, 0, 0, KEY_DIVIDE, KEY_RETURN, 0, KEY_SUBTRACT, KEY_F18,
+ KEY_F19, KEY_EQUAL, 0, 0, 0, 0, 0, 0,
+ 0, 0, KEY_F20, 0, 0, 0, 0, 0,
+ KEY_F5, KEY_F6, KEY_F7, KEY_F3, KEY_F8, KEY_F9, 0, KEY_F11,
+ 0, KEY_F13, KEY_F16, KEY_F14, 0, KEY_F10, 0, KEY_F12,
+ 0, KEY_F15, KEY_HELP, KEY_HOME, KEY_PAGEUP, KEY_DELETE, KEY_F4, KEY_END,
+ KEY_F2, KEY_PAGEDOWN, KEY_F1, KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP, 0
+ };
+
+ if (nKeyCode < SAL_N_ELEMENTS(aKeyCodeMap))
+ return aKeyCodeMap[nKeyCode];
+ return 0;
+}
+
+// store the frame the mouse last entered
+static AquaSalFrame* s_pMouseFrame = NULL;
+// store the last pressed button for enter/exit events
+// which lack that information
+static sal_uInt16 s_nLastButton = 0;
+
+// combinations of keys we need to handle ourselves
+static const struct ExceptionalKey
+{
+ const sal_uInt16 nKeyCode;
+ const unsigned int nModifierMask;
+} aExceptionalKeys[] =
+{
+ { KEY_D, NSControlKeyMask | NSShiftKeyMask | NSAlternateKeyMask },
+ { KEY_D, NSCommandKeyMask | NSShiftKeyMask | NSAlternateKeyMask }
+};
+
+static AquaSalFrame* getMouseContainerFrame()
+{
+ NSInteger nWindows = 0;
+ NSCountWindows( &nWindows );
+ NSInteger* pWindows = (NSInteger*)alloca( nWindows * sizeof(NSInteger) );
+ // note: NSWindowList is supposed to be in z-order front to back
+ NSWindowList( nWindows, pWindows );
+ AquaSalFrame* pDispatchFrame = NULL;
+ for(int i = 0; i < nWindows && ! pDispatchFrame; i++ )
+ {
+ NSWindow* pWin = [NSApp windowWithWindowNumber: pWindows[i]];
+ if( pWin && [pWin isMemberOfClass: [SalFrameWindow class]] && [(SalFrameWindow*)pWin containsMouse] )
+ pDispatchFrame = [(SalFrameWindow*)pWin getSalFrame];
+ }
+ return pDispatchFrame;
+}
+
+@implementation SalFrameWindow
+-(id)initWithSalFrame: (AquaSalFrame*)pFrame
+{
+ mDraggingDestinationHandler = nil;
+ mpFrame = pFrame;
+ NSRect aRect = { { static_cast<CGFloat>(pFrame->maGeometry.nX), static_cast<CGFloat>(pFrame->maGeometry.nY) },
+ { static_cast<CGFloat>(pFrame->maGeometry.nWidth), static_cast<CGFloat>(pFrame->maGeometry.nHeight) } };
+ pFrame->VCLToCocoa( aRect );
+ NSWindow* pNSWindow = [super initWithContentRect: aRect styleMask: mpFrame->getStyleMask() backing: NSBackingStoreBuffered defer: NO ];
+ [pNSWindow useOptimizedDrawing: YES]; // OSX recommendation when there are no overlapping subviews within the receiver
+
+ // enable OSX>=10.7 fullscreen options if available and useful
+ bool bAllowFullScreen = (0 == (mpFrame->mnStyle & (SAL_FRAME_STYLE_DIALOG | SAL_FRAME_STYLE_TOOLTIP | SAL_FRAME_STYLE_SYSTEMCHILD | SAL_FRAME_STYLE_FLOAT | SAL_FRAME_STYLE_TOOLWINDOW | SAL_FRAME_STYLE_INTRO)));
+ bAllowFullScreen &= (0 == (~mpFrame->mnStyle & (SAL_FRAME_STYLE_SIZEABLE)));
+ bAllowFullScreen &= (mpFrame->mpParent == NULL);
+ const SEL setCollectionBehavior = @selector(setCollectionBehavior:);
+ if( bAllowFullScreen && [pNSWindow respondsToSelector: setCollectionBehavior])
+ {
+ const int bMode= (bAllowFullScreen ? NSWindowCollectionBehaviorFullScreenPrimary : NSWindowCollectionBehaviorFullScreenAuxiliary);
+ [pNSWindow performSelector:setCollectionBehavior withObject:(id)(intptr_t)bMode];
+ }
+
+ // disable OSX>=10.7 window restoration until we support it directly
+ const SEL setRestorable = @selector(setRestorable:);
+ if( [pNSWindow respondsToSelector: setRestorable]) {
+ [pNSWindow performSelector:setRestorable withObject:(id)NO];
+ }
+
+ return (SalFrameWindow *) pNSWindow;
+}
+
+-(AquaSalFrame*)getSalFrame
+{
+ return mpFrame;
+}
+
+-(void)displayIfNeeded
+{
+ if( GetSalData() && GetSalData()->mpFirstInstance )
+ {
+ comphelper::SolarMutex* pMutex = GetSalData()->mpFirstInstance->GetYieldMutex();
+ if( pMutex )
+ {
+ pMutex->acquire();
+ [super displayIfNeeded];
+ pMutex->release();
+ }
+ }
+}
+
+-(BOOL)containsMouse
+{
+ // is this event actually inside that NSWindow ?
+ NSPoint aPt = [NSEvent mouseLocation];
+ NSRect aFrameRect = [self frame];
+ BOOL bInRect = NSPointInRect( aPt, aFrameRect );
+ return bInRect;
+}
+
+-(BOOL)canBecomeKeyWindow
+{
+ if( (mpFrame->mnStyle &
+ ( SAL_FRAME_STYLE_FLOAT |
+ SAL_FRAME_STYLE_TOOLTIP |
+ SAL_FRAME_STYLE_INTRO
+ )) == 0 )
+ return YES;
+ if( (mpFrame->mnStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) != 0 )
+ return YES;
+ if( mpFrame->mbFullScreen )
+ return YES;
+ if( (mpFrame->mnStyle & SAL_FRAME_STYLE_FLOAT_FOCUSABLE) )
+ return YES;
+ return [super canBecomeKeyWindow];
+}
+
+-(void)windowDidBecomeKey: (NSNotification*)pNotification
+{
+ (void)pNotification;
+ YIELD_GUARD;
+
+ if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
+ {
+ static const sal_uLong nGuessDocument = SAL_FRAME_STYLE_MOVEABLE|
+ SAL_FRAME_STYLE_SIZEABLE|
+ SAL_FRAME_STYLE_CLOSEABLE;
+
+ if( mpFrame->mpMenu )
+ mpFrame->mpMenu->setMainMenu();
+ else if( ! mpFrame->mpParent &&
+ ( (mpFrame->mnStyle & nGuessDocument) == nGuessDocument || // set default menu for e.g. help
+ mpFrame->mbFullScreen ) ) // ser default menu for e.g. presentation
+ {
+ AquaSalMenu::setDefaultMenu();
+ }
+ #if 0
+ // FIXME: we should disable menus while in modal mode
+ // however from down here there is currently no reliable way to
+ // find out when to do this
+ if( (mpFrame->mpParent && mpFrame->mpParent->GetWindow()->IsInModalMode()) )
+ AquaSalMenu::enableMainMenu( false );
+ #endif
+ mpFrame->CallCallback( SALEVENT_GETFOCUS, 0 );
+ mpFrame->SendPaintEvent(); // repaint controls as active
+ }
+}
+
+-(void)windowDidResignKey: (NSNotification*)pNotification
+{
+ (void)pNotification;
+ YIELD_GUARD;
+
+ if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
+ {
+ mpFrame->CallCallback(SALEVENT_LOSEFOCUS, 0);
+ mpFrame->SendPaintEvent(); // repaint controls as inactive
+ }
+}
+
+-(void)windowDidChangeScreen: (NSNotification*)pNotification
+{
+ (void)pNotification;
+ YIELD_GUARD;
+
+ if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
+ mpFrame->screenParametersChanged();
+}
+
+-(void)windowDidMove: (NSNotification*)pNotification
+{
+ (void)pNotification;
+ YIELD_GUARD;
+
+ if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
+ {
+ mpFrame->UpdateFrameGeometry();
+ mpFrame->CallCallback( SALEVENT_MOVE, 0 );
+ }
+}
+
+-(void)windowDidResize: (NSNotification*)pNotification
+{
+ (void)pNotification;
+ YIELD_GUARD;
+
+ if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
+ {
+ mpFrame->UpdateFrameGeometry();
+ mpFrame->CallCallback( SALEVENT_RESIZE, 0 );
+ mpFrame->SendPaintEvent();
+ }
+}
+
+-(void)windowDidMiniaturize: (NSNotification*)pNotification
+{
+ (void)pNotification;
+ YIELD_GUARD;
+
+ if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
+ {
+ mpFrame->mbShown = false;
+ mpFrame->UpdateFrameGeometry();
+ mpFrame->CallCallback( SALEVENT_RESIZE, 0 );
+ }
+}
+
+-(void)windowDidDeminiaturize: (NSNotification*)pNotification
+{
+ (void)pNotification;
+ YIELD_GUARD;
+
+ if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
+ {
+ mpFrame->mbShown = true;
+ mpFrame->UpdateFrameGeometry();
+ mpFrame->CallCallback( SALEVENT_RESIZE, 0 );
+ }
+}
+
+-(BOOL)windowShouldClose: (NSNotification*)pNotification
+{
+ (void)pNotification;
+ YIELD_GUARD;
+
+ BOOL bRet = YES;
+ if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
+ {
+ // #i84461# end possible input
+ mpFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, 0 );
+ if( AquaSalFrame::isAlive( mpFrame ) )
+ {
+ mpFrame->CallCallback( SALEVENT_CLOSE, 0 );
+ bRet = NO; // application will close the window or not, AppKit shouldn't
+ }
+ }
+
+ return bRet;
+}
+
+-(void)windowDidEnterFullScreen: (NSNotification*)pNotification
+{
+ YIELD_GUARD;
+
+ if( !mpFrame || !AquaSalFrame::isAlive( mpFrame))
+ return;
+ mpFrame->mbFullScreen = true;
+ (void)pNotification;
+}
+
+-(void)windowDidExitFullScreen: (NSNotification*)pNotification
+{
+ YIELD_GUARD;
+
+ if( !mpFrame || !AquaSalFrame::isAlive( mpFrame))
+ return;
+ mpFrame->mbFullScreen = false;
+ (void)pNotification;
+}
+
+-(void)dockMenuItemTriggered: (id)sender
+{
+ (void)sender;
+ YIELD_GUARD;
+
+ if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
+ mpFrame->ToTop( SAL_FRAME_TOTOP_RESTOREWHENMIN | SAL_FRAME_TOTOP_GRABFOCUS );
+}
+
+-(::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleContext >)accessibleContext
+{
+ return mpFrame -> GetWindow() -> GetAccessible() -> getAccessibleContext();
+}
+
+-(NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
+{
+ return [mDraggingDestinationHandler draggingEntered: sender];
+}
+
+-(NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
+{
+ return [mDraggingDestinationHandler draggingUpdated: sender];
+}
+
+-(void)draggingExited:(id <NSDraggingInfo>)sender
+{
+ [mDraggingDestinationHandler draggingExited: sender];
+}
+
+-(BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
+{
+ return [mDraggingDestinationHandler prepareForDragOperation: sender];
+}
+
+-(BOOL)performDragOperation:(id <NSDraggingInfo>)sender
+{
+ return [mDraggingDestinationHandler performDragOperation: sender];
+}
+
+-(void)concludeDragOperation:(id <NSDraggingInfo>)sender
+{
+ [mDraggingDestinationHandler concludeDragOperation: sender];
+}
+
+-(void)registerDraggingDestinationHandler:(id)theHandler
+{
+ mDraggingDestinationHandler = theHandler;
+}
+
+-(void)unregisterDraggingDestinationHandler:(id)theHandler
+{
+ (void)theHandler;
+ mDraggingDestinationHandler = nil;
+}
+
+@end
+
+@implementation SalFrameView
++(void)unsetMouseFrame: (AquaSalFrame*)pFrame
+{
+ if( pFrame == s_pMouseFrame )
+ s_pMouseFrame = NULL;
+}
+
+-(id)initWithSalFrame: (AquaSalFrame*)pFrame
+{
+ if ((self = [super initWithFrame: [NSWindow contentRectForFrameRect: [pFrame->getNSWindow() frame] styleMask: pFrame->mnStyleMask]]) != nil)
+ {
+ mDraggingDestinationHandler = nil;
+ mpFrame = pFrame;
+ mMarkedRange = NSMakeRange(NSNotFound, 0);
+ mSelectedRange = NSMakeRange(NSNotFound, 0);
+ mpReferenceWrapper = nil;
+ mpMouseEventListener = nil;
+ mpLastSuperEvent = nil;
+ }
+
+ mfLastMagnifyTime = 0.0;
+ return self;
+}
+
+-(AquaSalFrame*)getSalFrame
+{
+ return mpFrame;
+}
+
+-(void)resetCursorRects
+{
+ if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
+ {
+ // FIXME: does this leak the returned NSCursor of getCurrentCursor ?
+ NSRect aRect = { { 0, 0 }, { static_cast<CGFloat>(mpFrame->maGeometry.nWidth), static_cast<CGFloat>(mpFrame->maGeometry.nHeight) } };
+ [self addCursorRect: aRect cursor: mpFrame->getCurrentCursor()];
+ }
+}
+
+-(BOOL)acceptsFirstResponder
+{
+ return YES;
+}
+
+-(BOOL)acceptsFirstMouse: (NSEvent*)pEvent
+{
+ (void)pEvent;
+ return YES;
+}
+
+-(BOOL)isOpaque
+{
+ if( !mpFrame)
+ return YES;
+ if( !AquaSalFrame::isAlive( mpFrame))
+ return YES;
+ if( !mpFrame->getClipPath())
+ return YES;
+ return NO;
+}
+
+// helper class similar to a osl::Guard< comphelper::SolarMutex > for the
+// SalYieldMutex; the difference is that it only does tryToAcquire instead of
+// acquire so dreaded deadlocks like #i93512# are prevented
+class TryGuard
+{
+public:
+ TryGuard() { mbGuarded = ImplSalYieldMutexTryToAcquire(); }
+ ~TryGuard() { if( mbGuarded ) ImplSalYieldMutexRelease(); }
+ bool IsGuarded() { return mbGuarded; }
+private:
+ bool mbGuarded;
+};
+
+-(void)drawRect: (NSRect)aRect
+{
+ // HOTFIX: #i93512# prevent deadlocks if any other thread already has the SalYieldMutex
+ TryGuard aTryGuard;
+ if( !aTryGuard.IsGuarded() )
+ {
+ // NOTE: the mpFrame access below is not guarded yet!
+ // TODO: mpFrame et al need to be guarded by an independent mutex
+ AquaSalGraphics* pGraphics = (mpFrame && AquaSalFrame::isAlive(mpFrame)) ? mpFrame->mpGraphics : NULL;
+ if( pGraphics )
+ {
+ // we did not get the mutex so we cannot draw now => request to redraw later
+ // convert the NSRect to a CGRect for Refreshrect()
+ const CGRect aCGRect = {{aRect.origin.x,aRect.origin.y},{aRect.size.width,aRect.size.height}};
+ pGraphics->RefreshRect( aCGRect );
+ }
+ return;
+ }
+
+ if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
+ {
+ if( mpFrame->mpGraphics )
+ {
+ mpFrame->mpGraphics->UpdateWindow( aRect );
+ if( mpFrame->getClipPath() )
+ [mpFrame->getNSWindow() invalidateShadow];
+ }
+ }
+}
+
+-(void)sendMouseEventToFrame: (NSEvent*)pEvent button:(sal_uInt16)nButton eventtype:(sal_uInt16)nEvent
+{
+ YIELD_GUARD;
+
+ AquaSalFrame* pDispatchFrame = AquaSalFrame::GetCaptureFrame();
+ bool bIsCaptured = false;
+ if( pDispatchFrame )
+ {
+ bIsCaptured = true;
+ if( nEvent == SALEVENT_MOUSELEAVE ) // no leave events if mouse is captured
+ nEvent = SALEVENT_MOUSEMOVE;
+ }
+ else if( s_pMouseFrame )
+ pDispatchFrame = s_pMouseFrame;
+ else
+ pDispatchFrame = mpFrame;
+
+ /* #i81645# Cocoa reports mouse events while a button is pressed
+ to the window in which it was first pressed. This is reasonable and fine and
+ gets one around most cases where on other platforms one uses CaptureMouse or XGrabPointer,
+ however vcl expects mouse events to occur in the window the mouse is over, unless the
+ mouse is explicitly captured. So we need to find the window the mouse is actually
+ over for conformance with other platforms.
+ */
+ if( ! bIsCaptured && nButton && pDispatchFrame && AquaSalFrame::isAlive( pDispatchFrame ) )
+ {
+ // is this event actually inside that NSWindow ?
+ NSPoint aPt = [NSEvent mouseLocation];
+ NSRect aFrameRect = [pDispatchFrame->getNSWindow() frame];
+
+ if ( ! NSPointInRect( aPt, aFrameRect ) )
+ {
+ // no, it is not
+ // now we need to find the one it may be in
+ /* #i93756# we ant to get enumerate the application windows in z-order
+ to check if any contains the mouse. This could be elegantly done with this
+ code:
+
+ // use NSApp to check windows in ZOrder whether they contain the mouse pointer
+ NSWindow* pWindow = [NSApp makeWindowsPerform: @selector(containsMouse) inOrder: YES];
+ if( pWindow && [pWindow isMemberOfClass: [SalFrameWindow class]] )
+ pDispatchFrame = [(SalFrameWindow*)pWindow getSalFrame];
+
+ However if a non SalFrameWindow is on screen (like e.g. the file dialog)
+ it can be hit with the containsMouse selector, which it doesn't support.
+ Sadly NSApplication:makeWindowsPerform does not check (for performance reasons
+ I assume) whether a window supports a selector before sending it.
+ */
+ AquaSalFrame* pMouseFrame = getMouseContainerFrame();
+ if( pMouseFrame )
+ pDispatchFrame = pMouseFrame;
+ }
+ }
+
+ if( pDispatchFrame && AquaSalFrame::isAlive( pDispatchFrame ) )
+ {
+ pDispatchFrame->mnLastEventTime = static_cast<sal_uLong>( [pEvent timestamp] * 1000.0 );
+ pDispatchFrame->mnLastModifierFlags = [pEvent modifierFlags];
+
+ NSPoint aPt = [NSEvent mouseLocation];
+ pDispatchFrame->CocoaToVCL( aPt );
+
+ sal_uInt16 nModMask = ImplGetModifierMask( [pEvent modifierFlags] );
+ // #i82284# emulate ctrl left
+ if( nModMask == KEY_MOD3 && nButton == MOUSE_LEFT )
+ {
+ nModMask = 0;
+ nButton = MOUSE_RIGHT;
+ }
+
+ SalMouseEvent aEvent;
+ aEvent.mnTime = pDispatchFrame->mnLastEventTime;
+ aEvent.mnX = static_cast<long>(aPt.x) - pDispatchFrame->maGeometry.nX;
+ aEvent.mnY = static_cast<long>(aPt.y) - pDispatchFrame->maGeometry.nY;
+ aEvent.mnButton = nButton;
+ aEvent.mnCode = aEvent.mnButton | nModMask;
+
+ // --- RTL --- (mirror mouse pos)
+ if( Application::GetSettings().GetLayoutRTL() )
+ aEvent.mnX = pDispatchFrame->maGeometry.nWidth-1-aEvent.mnX;
+
+ pDispatchFrame->CallCallback( nEvent, &aEvent );
+ }
+}
+
+-(void)mouseDown: (NSEvent*)pEvent
+{
+ if ( mpMouseEventListener != nil &&
+ [mpMouseEventListener respondsToSelector: @selector(mouseDown:)])
+ {
+ [mpMouseEventListener mouseDown: [pEvent copyWithZone: NULL]];
+ }
+
+ s_nLastButton = MOUSE_LEFT;
+ [self sendMouseEventToFrame:pEvent button:MOUSE_LEFT eventtype:SALEVENT_MOUSEBUTTONDOWN];
+}
+
+-(void)mouseDragged: (NSEvent*)pEvent
+{
+ if ( mpMouseEventListener != nil &&
+ [mpMouseEventListener respondsToSelector: @selector(mouseDragged:)])
+ {
+ [mpMouseEventListener mouseDragged: [pEvent copyWithZone: NULL]];
+ }
+ s_nLastButton = MOUSE_LEFT;
+ [self sendMouseEventToFrame:pEvent button:MOUSE_LEFT eventtype:SALEVENT_MOUSEMOVE];
+}
+
+-(void)mouseUp: (NSEvent*)pEvent
+{
+ s_nLastButton = 0;
+ [self sendMouseEventToFrame:pEvent button:MOUSE_LEFT eventtype:SALEVENT_MOUSEBUTTONUP];
+}
+
+-(void)mouseMoved: (NSEvent*)pEvent
+{
+ s_nLastButton = 0;
+ [self sendMouseEventToFrame:pEvent button:0 eventtype:SALEVENT_MOUSEMOVE];
+}
+
+-(void)mouseEntered: (NSEvent*)pEvent
+{
+ s_pMouseFrame = mpFrame;
+
+ // #i107215# the only mouse events we get when inactive are enter/exit
+ // actually we would like to have all of them, but better none than some
+ if( [NSApp isActive] )
+ [self sendMouseEventToFrame:pEvent button:s_nLastButton eventtype:SALEVENT_MOUSEMOVE];
+}
+
+-(void)mouseExited: (NSEvent*)pEvent
+{
+ if( s_pMouseFrame == mpFrame )
+ s_pMouseFrame = NULL;
+
+ // #i107215# the only mouse events we get when inactive are enter/exit
+ // actually we would like to have all of them, but better none than some
+ if( [NSApp isActive] )
+ [self sendMouseEventToFrame:pEvent button:s_nLastButton eventtype:SALEVENT_MOUSELEAVE];
+}
+
+-(void)rightMouseDown: (NSEvent*)pEvent
+{
+ s_nLastButton = MOUSE_RIGHT;
+ [self sendMouseEventToFrame:pEvent button:MOUSE_RIGHT eventtype:SALEVENT_MOUSEBUTTONDOWN];
+}
+
+-(void)rightMouseDragged: (NSEvent*)pEvent
+{
+ s_nLastButton = MOUSE_RIGHT;
+ [self sendMouseEventToFrame:pEvent button:MOUSE_RIGHT eventtype:SALEVENT_MOUSEMOVE];
+}
+
+-(void)rightMouseUp: (NSEvent*)pEvent
+{
+ s_nLastButton = 0;
+ [self sendMouseEventToFrame:pEvent button:MOUSE_RIGHT eventtype:SALEVENT_MOUSEBUTTONUP];
+}
+
+-(void)otherMouseDown: (NSEvent*)pEvent
+{
+ if( [pEvent buttonNumber] == 2 )
+ {
+ s_nLastButton = MOUSE_MIDDLE;
+ [self sendMouseEventToFrame:pEvent button:MOUSE_MIDDLE eventtype:SALEVENT_MOUSEBUTTONDOWN];
+ }
+ else
+ s_nLastButton = 0;
+}
+
+-(void)otherMouseDragged: (NSEvent*)pEvent
+{
+ if( [pEvent buttonNumber] == 2 )
+ {
+ s_nLastButton = MOUSE_MIDDLE;
+ [self sendMouseEventToFrame:pEvent button:MOUSE_MIDDLE eventtype:SALEVENT_MOUSEMOVE];
+ }
+ else
+ s_nLastButton = 0;
+}
+
+-(void)otherMouseUp: (NSEvent*)pEvent
+{
+ s_nLastButton = 0;
+ if( [pEvent buttonNumber] == 2 )
+ [self sendMouseEventToFrame:pEvent button:MOUSE_MIDDLE eventtype:SALEVENT_MOUSEBUTTONUP];
+}
+
+- (void)magnifyWithEvent: (NSEvent*)pEvent
+{
+ YIELD_GUARD;
+
+ // TODO: ?? -(float)magnification;
+ if( AquaSalFrame::isAlive( mpFrame ) )
+ {
+ const NSTimeInterval fMagnifyTime = [pEvent timestamp];
+ mpFrame->mnLastEventTime = static_cast<sal_uLong>( fMagnifyTime * 1000.0 );
+ mpFrame->mnLastModifierFlags = [pEvent modifierFlags];
+
+ // check if this is a new series of magnify events
+ static const NSTimeInterval fMaxDiffTime = 0.3;
+ const bool bNewSeries = (fMagnifyTime - mfLastMagnifyTime > fMaxDiffTime);
+
+ if( bNewSeries )
+ mfMagnifyDeltaSum = 0.0;
+ mfMagnifyDeltaSum += [pEvent deltaZ];
+
+ mfLastMagnifyTime = [pEvent timestamp];
+ // TODO: change to 0.1 when COMMAND_WHEEL_ZOOM handlers allow finer zooming control
+ static const float fMagnifyFactor = 0.25;
+ static const float fMinMagnifyStep = 15.0 / fMagnifyFactor;
+ if( fabs(mfMagnifyDeltaSum) <= fMinMagnifyStep )
+ return;
+
+ // adapt NSEvent-sensitivity to application expectations
+ // TODO: rather make COMMAND_WHEEL_ZOOM handlers smarter
+ const float fDeltaZ = mfMagnifyDeltaSum * fMagnifyFactor;
+ int nDeltaZ = FRound( fDeltaZ );
+ if( !nDeltaZ )
+ {
+ // handle new series immediately
+ if( !bNewSeries )
+ return;
+ nDeltaZ = (fDeltaZ >= 0.0) ? +1 : -1;
+ }
+ // eventually give credit for delta sum
+ mfMagnifyDeltaSum -= nDeltaZ / fMagnifyFactor;
+
+ NSPoint aPt = [NSEvent mouseLocation];
+ mpFrame->CocoaToVCL( aPt );
+
+ SalWheelMouseEvent aEvent;
+ aEvent.mnTime = mpFrame->mnLastEventTime;
+ aEvent.mnX = static_cast<long>(aPt.x) - mpFrame->maGeometry.nX;
+ aEvent.mnY = static_cast<long>(aPt.y) - mpFrame->maGeometry.nY;
+ aEvent.mnCode = ImplGetModifierMask( mpFrame->mnLastModifierFlags );
+ aEvent.mnCode |= KEY_MOD1; // we want zooming, no scrolling
+ aEvent.mbDeltaIsPixel = TRUE;
+
+ // --- RTL --- (mirror mouse pos)
+ if( Application::GetSettings().GetLayoutRTL() )
+ aEvent.mnX = mpFrame->maGeometry.nWidth-1-aEvent.mnX;
+
+ aEvent.mnDelta = nDeltaZ;
+ aEvent.mnNotchDelta = (nDeltaZ >= 0) ? +1 : -1;
+ if( aEvent.mnDelta == 0 )
+ aEvent.mnDelta = aEvent.mnNotchDelta;
+ aEvent.mbHorz = FALSE;
+ aEvent.mnScrollLines = nDeltaZ;
+ if( aEvent.mnScrollLines == 0 )
+ aEvent.mnScrollLines = 1;
+ mpFrame->CallCallback( SALEVENT_WHEELMOUSE, &aEvent );
+ }
+}
+
+- (void)rotateWithEvent: (NSEvent*)pEvent
+{
+ //Rotation : -(float)rotation;
+ // TODO: create new CommandType so rotation is available to the applications
+ (void)pEvent;
+}
+
+- (void)swipeWithEvent: (NSEvent*)pEvent
+{
+ YIELD_GUARD;
+
+ if( AquaSalFrame::isAlive( mpFrame ) )
+ {
+ mpFrame->mnLastEventTime = static_cast<sal_uLong>( [pEvent timestamp] * 1000.0 );
+ mpFrame->mnLastModifierFlags = [pEvent modifierFlags];
+
+ // merge pending scroll wheel events
+ float dX = 0.0;
+ float dY = 0.0;
+ for(;;)
+ {
+ dX += [pEvent deltaX];
+ dY += [pEvent deltaY];
+ NSEvent* pNextEvent = [NSApp nextEventMatchingMask: NSScrollWheelMask
+ untilDate: nil inMode: NSDefaultRunLoopMode dequeue: YES ];
+ if( !pNextEvent )
+ break;
+ pEvent = pNextEvent;
+ }
+
+ NSPoint aPt = [NSEvent mouseLocation];
+ mpFrame->CocoaToVCL( aPt );
+
+ SalWheelMouseEvent aEvent;
+ aEvent.mnTime = mpFrame->mnLastEventTime;
+ aEvent.mnX = static_cast<long>(aPt.x) - mpFrame->maGeometry.nX;
+ aEvent.mnY = static_cast<long>(aPt.y) - mpFrame->maGeometry.nY;
+ aEvent.mnCode = ImplGetModifierMask( mpFrame->mnLastModifierFlags );
+ aEvent.mbDeltaIsPixel = TRUE;
+
+ // --- RTL --- (mirror mouse pos)
+ if( Application::GetSettings().GetLayoutRTL() )
+ aEvent.mnX = mpFrame->maGeometry.nWidth-1-aEvent.mnX;
+
+ if( dX != 0.0 )
+ {
+ aEvent.mnDelta = static_cast<long>(floor(dX));
+ aEvent.mnNotchDelta = dX < 0 ? -1 : 1;
+ if( aEvent.mnDelta == 0 )
+ aEvent.mnDelta = aEvent.mnNotchDelta;
+ aEvent.mbHorz = TRUE;
+ aEvent.mnScrollLines = SAL_WHEELMOUSE_EVENT_PAGESCROLL;
+ mpFrame->CallCallback( SALEVENT_WHEELMOUSE, &aEvent );
+ }
+ if( dY != 0.0 && AquaSalFrame::isAlive( mpFrame ))
+ {
+ aEvent.mnDelta = static_cast<long>(floor(dY));
+ aEvent.mnNotchDelta = dY < 0 ? -1 : 1;
+ if( aEvent.mnDelta == 0 )
+ aEvent.mnDelta = aEvent.mnNotchDelta;
+ aEvent.mbHorz = FALSE;
+ aEvent.mnScrollLines = SAL_WHEELMOUSE_EVENT_PAGESCROLL;
+ mpFrame->CallCallback( SALEVENT_WHEELMOUSE, &aEvent );
+ }
+ }
+}
+
+-(void)scrollWheel: (NSEvent*)pEvent
+{
+ YIELD_GUARD;
+
+ if( AquaSalFrame::isAlive( mpFrame ) )
+ {
+ mpFrame->mnLastEventTime = static_cast<sal_uLong>( [pEvent timestamp] * 1000.0 );
+ mpFrame->mnLastModifierFlags = [pEvent modifierFlags];
+
+ // merge pending scroll wheel events
+ float dX = 0.0;
+ float dY = 0.0;
+ for(;;)
+ {
+ dX += [pEvent deltaX];
+ dY += [pEvent deltaY];
+ NSEvent* pNextEvent = [NSApp nextEventMatchingMask: NSScrollWheelMask
+ untilDate: nil inMode: NSDefaultRunLoopMode dequeue: YES ];
+ if( !pNextEvent )
+ break;
+ pEvent = pNextEvent;
+ }
+
+ NSPoint aPt = [NSEvent mouseLocation];
+ mpFrame->CocoaToVCL( aPt );
+
+ SalWheelMouseEvent aEvent;
+ aEvent.mnTime = mpFrame->mnLastEventTime;
+ aEvent.mnX = static_cast<long>(aPt.x) - mpFrame->maGeometry.nX;
+ aEvent.mnY = static_cast<long>(aPt.y) - mpFrame->maGeometry.nY;
+ aEvent.mnCode = ImplGetModifierMask( mpFrame->mnLastModifierFlags );
+ aEvent.mbDeltaIsPixel = FALSE;
+
+ // --- RTL --- (mirror mouse pos)
+ if( Application::GetSettings().GetLayoutRTL() )
+ aEvent.mnX = mpFrame->maGeometry.nWidth-1-aEvent.mnX;
+
+ if( dX != 0.0 )
+ {
+ aEvent.mnDelta = static_cast<long>(floor(dX));
+ aEvent.mnNotchDelta = dX < 0 ? -1 : 1;
+ if( aEvent.mnDelta == 0 )
+ aEvent.mnDelta = aEvent.mnNotchDelta;
+ aEvent.mbHorz = TRUE;
+ aEvent.mnScrollLines = dX > 0 ? dX/WHEEL_EVENT_FACTOR : -dX/WHEEL_EVENT_FACTOR;
+ if( aEvent.mnScrollLines == 0 )
+ aEvent.mnScrollLines = 1;
+
+ mpFrame->CallCallback( SALEVENT_WHEELMOUSE, &aEvent );
+ }
+ if( dY != 0.0 && AquaSalFrame::isAlive( mpFrame ) )
+ {
+ aEvent.mnDelta = static_cast<long>(floor(dY));
+ aEvent.mnNotchDelta = dY < 0 ? -1 : 1;
+ if( aEvent.mnDelta == 0 )
+ aEvent.mnDelta = aEvent.mnNotchDelta;
+ aEvent.mbHorz = FALSE;
+ aEvent.mnScrollLines = dY > 0 ? dY/WHEEL_EVENT_FACTOR : -dY/WHEEL_EVENT_FACTOR;
+ if( aEvent.mnScrollLines < 1 )
+ aEvent.mnScrollLines = 1;
+
+ mpFrame->CallCallback( SALEVENT_WHEELMOUSE, &aEvent );
+ }
+ }
+}
+
+
+-(void)keyDown: (NSEvent*)pEvent
+{
+ YIELD_GUARD;
+
+ if( AquaSalFrame::isAlive( mpFrame ) )
+ {
+ mpLastEvent = pEvent;
+ mbInKeyInput = true;
+ mbNeedSpecialKeyHandle = false;
+ mbKeyHandled = false;
+
+ mpFrame->mnLastEventTime = static_cast<sal_uLong>( [pEvent timestamp] * 1000.0 );
+ mpFrame->mnLastModifierFlags = [pEvent modifierFlags];
+
+ if( ! [self handleKeyDownException: pEvent] )
+ {
+ NSArray* pArray = [NSArray arrayWithObject: pEvent];
+ [self interpretKeyEvents: pArray];
+ }
+
+ mbInKeyInput = false;
+ }
+}
+
+-(BOOL)handleKeyDownException:(NSEvent*)pEvent
+{
+ // check for a very special set of modified characters
+ NSString* pUnmodifiedString = [pEvent charactersIgnoringModifiers];
+
+ if( pUnmodifiedString && [pUnmodifiedString length] == 1 )
+ {
+ /* #i103102# key events with command and alternate don't make it through
+ interpretKeyEvents (why ?). Try to dispatch them here first,
+ if not successful continue normally
+ */
+ if( (mpFrame->mnLastModifierFlags & (NSAlternateKeyMask | NSCommandKeyMask))
+ == (NSAlternateKeyMask | NSCommandKeyMask) )
+ {
+ if( [self sendSingleCharacter: mpLastEvent] )
+ return YES;
+ }
+ unichar keyChar = [pUnmodifiedString characterAtIndex: 0];
+ sal_uInt16 nKeyCode = ImplMapCharCode( keyChar );
+
+ // Caution: should the table grow to more than 5 or 6 entries,
+ // we must consider moving it to a kind of hash map
+ const unsigned int nExceptions = SAL_N_ELEMENTS( aExceptionalKeys );
+ for( unsigned int i = 0; i < nExceptions; i++ )
+ {
+ if( nKeyCode == aExceptionalKeys[i].nKeyCode &&
+ (mpFrame->mnLastModifierFlags & aExceptionalKeys[i].nModifierMask)
+ == aExceptionalKeys[i].nModifierMask )
+ {
+ [self sendKeyInputAndReleaseToFrame: nKeyCode character: 0];
+
+ return YES;
+ }
+ }
+ }
+ return NO;
+}
+
+-(void)flagsChanged: (NSEvent*)pEvent
+{
+ YIELD_GUARD;
+
+ if( AquaSalFrame::isAlive( mpFrame ) )
+ {
+ mpFrame->mnLastEventTime = static_cast<sal_uLong>( [pEvent timestamp] * 1000.0 );
+ mpFrame->mnLastModifierFlags = [pEvent modifierFlags];
+ }
+}
+
+-(void)insertText:(id)aString
+{
+ YIELD_GUARD;
+
+ if( AquaSalFrame::isAlive( mpFrame ) )
+ {
+ NSString* pInsert = nil;
+ if( [aString isMemberOfClass: [NSAttributedString class]] )
+ pInsert = [aString string];
+ else
+ pInsert = aString;
+
+ int nLen = 0;
+ if( pInsert && ( nLen = [pInsert length] ) > 0 )
+ {
+ OUString aInsertString( GetOUString( pInsert ) );
+ // aCharCode initializer is safe since aInsertString will at least contain '\0'
+ sal_Unicode aCharCode = *aInsertString.getStr();
+
+ if( nLen == 1 &&
+ aCharCode < 0x80 &&
+ aCharCode > 0x1f &&
+ ! [self hasMarkedText ]
+ )
+ {
+ sal_uInt16 nKeyCode = ImplMapCharCode( aCharCode );
+ unsigned int nLastModifiers = mpFrame->mnLastModifierFlags;
+
+ // #i99567#
+ // find out the unmodified key code
+
+ // sanity check
+ if( mpLastEvent && ( [mpLastEvent type] == NSKeyDown || [mpLastEvent type] == NSKeyUp ) )
+ {
+ // get unmodified string
+ NSString* pUnmodifiedString = [mpLastEvent charactersIgnoringModifiers];
+ if( pUnmodifiedString && [pUnmodifiedString length] == 1 )
+ {
+ // map the unmodified key code
+ unichar keyChar = [pUnmodifiedString characterAtIndex: 0];
+ nKeyCode = ImplMapCharCode( keyChar );
+ }
+ nLastModifiers = [mpLastEvent modifierFlags];
+
+ }
+ // #i99567#
+ // applications and vcl's edit fields ignore key events with ALT
+ // however we're at a place where we know text should be inserted
+ // so it seems we need to strip the Alt modifier here
+ if( (nLastModifiers & (NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask))
+ == NSAlternateKeyMask )
+ {
+ nLastModifiers = 0;
+ }
+ [self sendKeyInputAndReleaseToFrame: nKeyCode character: aCharCode modifiers: nLastModifiers];
+ }
+ else
+ {
+ SalExtTextInputEvent aEvent;
+ aEvent.mnTime = mpFrame->mnLastEventTime;
+ aEvent.maText = aInsertString;
+ aEvent.mpTextAttr = NULL;
+ aEvent.mnCursorPos = aInsertString.getLength();
+ aEvent.mnDeltaStart = 0;
+ aEvent.mnCursorFlags = 0;
+ aEvent.mbOnlyCursor = FALSE;
+ mpFrame->CallCallback( SALEVENT_EXTTEXTINPUT, &aEvent );
+ if( AquaSalFrame::isAlive( mpFrame ) )
+ mpFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, 0 );
+ }
+ }
+ else
+ {
+ SalExtTextInputEvent aEvent;
+ aEvent.mnTime = mpFrame->mnLastEventTime;
+ aEvent.maText = OUString();
+ aEvent.mpTextAttr = NULL;
+ aEvent.mnCursorPos = 0;
+ aEvent.mnDeltaStart = 0;
+ aEvent.mnCursorFlags = 0;
+ aEvent.mbOnlyCursor = FALSE;
+ mpFrame->CallCallback( SALEVENT_EXTTEXTINPUT, &aEvent );
+ if( AquaSalFrame::isAlive( mpFrame ) )
+ mpFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, 0 );
+
+ }
+ mbKeyHandled = true;
+ [self unmarkText];
+ }
+}
+
+-(void)insertTab: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: KEY_TAB character: '\t' modifiers: 0];
+}
+
+-(void)insertBacktab: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: (KEY_TAB | KEY_SHIFT) character: '\t' modifiers: 0];
+}
+
+-(void)moveLeft: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: KEY_LEFT character: 0 modifiers: 0];
+}
+
+-(void)moveLeftAndModifySelection: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: KEY_LEFT character: 0 modifiers: NSShiftKeyMask];
+}
+
+-(void)moveBackwardAndModifySelection: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_BACKWARD character: 0 modifiers: 0];
+}
+
+-(void)moveRight: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: KEY_RIGHT character: 0 modifiers: 0];
+}
+
+-(void)moveRightAndModifySelection: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: KEY_RIGHT character: 0 modifiers: NSShiftKeyMask];
+}
+
+-(void)moveForwardAndModifySelection: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_FORWARD character: 0 modifiers: 0];
+}
+
+-(void)moveWordLeft: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_WORD_BACKWARD character: 0 modifiers: 0];
+}
+
+-(void)moveWordBackward: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_WORD_BACKWARD character: 0 modifiers: 0];
+}
+
+-(void)moveWordBackwardAndModifySelection: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_WORD_BACKWARD character: 0 modifiers: 0];
+}
+
+-(void)moveWordLeftAndModifySelection: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_WORD_BACKWARD character: 0 modifiers: 0];
+}
+
+-(void)moveWordRight: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_WORD_FORWARD character: 0 modifiers: 0];
+}
+
+-(void)moveWordForward: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_WORD_FORWARD character: 0 modifiers: 0];
+}
+
+-(void)moveWordForwardAndModifySelection: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_WORD_FORWARD character: 0 modifiers: 0];
+}
+
+-(void)moveWordRightAndModifySelection: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_WORD_FORWARD character: 0 modifiers: 0];
+}
+
+-(void)moveToEndOfLine: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_END_OF_LINE character: 0 modifiers: 0];
+}
+
+-(void)moveToRightEndOfLine: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_END_OF_LINE character: 0 modifiers: 0];
+}
+
+-(void)moveToEndOfLineAndModifySelection: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_TO_END_OF_LINE character: 0 modifiers: 0];
+}
+
+-(void)moveToRightEndOfLineAndModifySelection: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_TO_END_OF_LINE character: 0 modifiers: 0];
+}
+
+-(void)moveToBeginningOfLine: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_LINE character: 0 modifiers: 0];
+}
+
+-(void)moveToLeftEndOfLine: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_LINE character: 0 modifiers: 0];
+}
+
+-(void)moveToBeginningOfLineAndModifySelection: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_LINE character: 0 modifiers: 0];
+}
+
+-(void)moveToLeftEndOfLineAndModifySelection: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_LINE character: 0 modifiers: 0];
+}
+
+-(void)moveToEndOfParagraph: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_END_OF_PARAGRAPH character: 0 modifiers: 0];
+}
+
+-(void)moveToEndOfParagraphAndModifySelection: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_TO_END_OF_PARAGRAPH character: 0 modifiers: 0];
+}
+
+-(void)moveParagraphForward: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_END_OF_PARAGRAPH character: 0 modifiers: 0];
+}
+
+-(void)moveParagraphForwardAndModifySelection: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_TO_END_OF_PARAGRAPH character: 0 modifiers: 0];
+}
+
+-(void)moveToBeginningOfParagraph: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH character: 0 modifiers: 0];
+}
+
+-(void)moveParagraphBackward: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH character: 0 modifiers: 0];
+}
+
+-(void)moveToBeginningOfParagraphAndModifySelection: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH character: 0 modifiers: 0];
+}
+
+-(void)moveParagraphBackwardAndModifySelection: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH character: 0 modifiers: 0];
+}
+
+-(void)moveToEndOfDocument: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_END_OF_DOCUMENT character: 0 modifiers: 0];
+}
+
+-(void)scrollToEndOfDocument: (id)aSender
+{
+ (void)aSender;
+ // this is not exactly what we should do, but it makes "End" and "Shift-End" behave consistent
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_END_OF_DOCUMENT character: 0 modifiers: 0];
+}
+
+-(void)moveToEndOfDocumentAndModifySelection: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_TO_END_OF_DOCUMENT character: 0 modifiers: 0];
+}
+
+-(void)moveToBeginningOfDocument: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT character: 0 modifiers: 0];
+}
+
+-(void)scrollToBeginningOfDocument: (id)aSender
+{
+ (void)aSender;
+ // this is not exactly what we should do, but it makes "Home" and "Shift-Home" behave consistent
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT character: 0 modifiers: 0];
+}
+
+-(void)moveToBeginningOfDocumentAndModifySelection: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT character: 0 modifiers: 0];
+}
+
+-(void)moveUp: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: KEY_UP character: 0 modifiers: 0];
+}
+
+-(void)moveDown: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: KEY_DOWN character: 0 modifiers: 0];
+}
+
+-(void)insertNewline: (id)aSender
+{
+ (void)aSender;
+ // #i91267# make enter and shift-enter work by evaluating the modifiers
+ [self sendKeyInputAndReleaseToFrame: KEY_RETURN character: '\n' modifiers: mpFrame->mnLastModifierFlags];
+}
+
+-(void)deleteBackward: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: KEY_BACKSPACE character: '\b' modifiers: 0];
+}
+
+-(void)deleteForward: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: KEY_DELETE character: 0x7f modifiers: 0];
+}
+
+-(void)deleteBackwardByDecomposingPreviousCharacter: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: KEY_BACKSPACE character: '\b' modifiers: 0];
+}
+
+-(void)deleteWordBackward: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::DELETE_WORD_BACKWARD character: 0 modifiers: 0];
+}
+
+-(void)deleteWordForward: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::DELETE_WORD_FORWARD character: 0 modifiers: 0];
+}
+
+-(void)deleteToBeginningOfLine: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::DELETE_TO_BEGIN_OF_LINE character: 0 modifiers: 0];
+}
+
+-(void)deleteToEndOfLine: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::DELETE_TO_END_OF_LINE character: 0 modifiers: 0];
+}
+
+-(void)deleteToBeginningOfParagraph: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::DELETE_TO_BEGIN_OF_PARAGRAPH character: 0 modifiers: 0];
+}
+
+-(void)deleteToEndOfParagraph: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::DELETE_TO_END_OF_PARAGRAPH character: 0 modifiers: 0];
+}
+
+-(void)insertLineBreak: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::INSERT_LINEBREAK character: 0 modifiers: 0];
+}
+
+-(void)insertParagraphSeparator: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::INSERT_PARAGRAPH character: 0 modifiers: 0];
+}
+
+-(void)selectWord: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_WORD character: 0 modifiers: 0];
+}
+
+-(void)selectLine: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_LINE character: 0 modifiers: 0];
+}
+
+-(void)selectParagraph: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_PARAGRAPH character: 0 modifiers: 0];
+}
+
+-(void)selectAll: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_ALL character: 0 modifiers: 0];
+}
+
+-(void)cancelOperation: (id)aSender
+{
+ (void)aSender;
+ [self sendKeyInputAndReleaseToFrame: KEY_ESCAPE character: 0x1b modifiers: 0];
+}
+
+-(void)noop: (id)aSender
+{
+ (void)aSender;
+ if( ! mbKeyHandled )
+ {
+ if( ! [self sendSingleCharacter:mpLastEvent] )
+ {
+ /* prevent recursion */
+ if( mpLastEvent != mpLastSuperEvent && [NSApp respondsToSelector: @selector(sendSuperEvent:)] )
+ {
+ id pLastSuperEvent = mpLastSuperEvent;
+ mpLastSuperEvent = mpLastEvent;
+ [NSApp performSelector:@selector(sendSuperEvent:) withObject: mpLastEvent];
+ mpLastSuperEvent = pLastSuperEvent;
+
+ std::map< NSEvent*, bool >::iterator it = GetSalData()->maKeyEventAnswer.find( mpLastEvent );
+ if( it != GetSalData()->maKeyEventAnswer.end() )
+ it->second = true;
+ }
+ }
+ }
+}
+
+-(BOOL)sendKeyInputAndReleaseToFrame: (sal_uInt16)nKeyCode character: (sal_Unicode)aChar
+{
+ return [self sendKeyInputAndReleaseToFrame: nKeyCode character: aChar modifiers: mpFrame->mnLastModifierFlags];
+}
+
+-(BOOL)sendKeyInputAndReleaseToFrame: (sal_uInt16)nKeyCode character: (sal_Unicode)aChar modifiers: (unsigned int)nMod
+{
+ return [self sendKeyToFrameDirect: nKeyCode character: aChar modifiers: nMod] ||
+ [self sendSingleCharacter: mpLastEvent];
+}
+
+-(BOOL)sendKeyToFrameDirect: (sal_uInt16)nKeyCode character: (sal_Unicode)aChar modifiers: (unsigned int)nMod
+{
+ YIELD_GUARD;
+
+ long nRet = 0;
+ if( AquaSalFrame::isAlive( mpFrame ) )
+ {
+ SalKeyEvent aEvent;
+ aEvent.mnTime = mpFrame->mnLastEventTime;
+ aEvent.mnCode = nKeyCode | ImplGetModifierMask( nMod );
+ aEvent.mnCharCode = aChar;
+ aEvent.mnRepeat = FALSE;
+ nRet = mpFrame->CallCallback( SALEVENT_KEYINPUT, &aEvent );
+ std::map< NSEvent*, bool >::iterator it = GetSalData()->maKeyEventAnswer.find( mpLastEvent );
+ if( it != GetSalData()->maKeyEventAnswer.end() )
+ it->second = nRet ? true : false;
+ if( AquaSalFrame::isAlive( mpFrame ) )
+ mpFrame->CallCallback( SALEVENT_KEYUP, &aEvent );
+ }
+ return nRet ? YES : NO;
+}
+
+
+-(BOOL)sendSingleCharacter: (NSEvent *)pEvent
+{
+ NSString* pUnmodifiedString = [pEvent charactersIgnoringModifiers];
+
+ if( pUnmodifiedString && [pUnmodifiedString length] == 1 )
+ {
+ unichar keyChar = [pUnmodifiedString characterAtIndex: 0];
+ sal_uInt16 nKeyCode = ImplMapCharCode( keyChar );
+ if (nKeyCode == 0)
+ {
+ sal_uInt16 nOtherKeyCode = [pEvent keyCode];
+ nKeyCode = ImplMapKeyCode(nOtherKeyCode);
+ }
+ if( nKeyCode != 0 )
+ {
+ // don't send unicodes in the private use area
+ if( keyChar >= 0xf700 && keyChar < 0xf780 )
+ keyChar = 0;
+ BOOL bRet = [self sendKeyToFrameDirect: nKeyCode character: keyChar modifiers: mpFrame->mnLastModifierFlags];
+ mbInKeyInput = false;
+
+ return bRet;
+ }
+ }
+ return NO;
+}
+
+
+// NSTextInput protocol
+- (NSArray *)validAttributesForMarkedText
+{
+ return [NSArray arrayWithObjects:NSUnderlineStyleAttributeName, nil];
+}
+
+- (BOOL)hasMarkedText
+{
+ BOOL bHasMarkedText;
+
+ bHasMarkedText = ( mMarkedRange.location != NSNotFound ) &&
+ ( mMarkedRange.length != 0 );
+ // hack to check keys like "Control-j"
+ if( mbInKeyInput )
+ {
+ mbNeedSpecialKeyHandle = true;
+ }
+
+ // FIXME:
+ // #i106901#
+ // if we come here outside of mbInKeyInput, this is likely to be because
+ // of the keyboard viewer. For unknown reasons having no marked range
+ // in this case causes a crash. So we say we have a marked range anyway
+ // This is a hack, since it is not understood what a) causes that crash
+ // and b) why we should have a marked range at this point.
+ if( ! mbInKeyInput )
+ bHasMarkedText = YES;
+
+ return bHasMarkedText;
+}
+
+- (NSRange)markedRange
+{
+ // FIXME:
+ // #i106901#
+ // if we come here outside of mbInKeyInput, this is likely to be because
+ // of the keyboard viewer. For unknown reasons having no marked range
+ // in this case causes a crash. So we say we have a marked range anyway
+ // This is a hack, since it is not understood what a) causes that crash
+ // and b) why we should have a marked range at this point.
+ if( ! mbInKeyInput )
+ return NSMakeRange( 0, 0 );
+
+ return [self hasMarkedText] ? mMarkedRange : NSMakeRange( NSNotFound, 0 );
+}
+
+- (NSRange)selectedRange
+{
+ return mSelectedRange;
+}
+
+- (void)setMarkedText:(id)aString selectedRange:(NSRange)selRange
+{
+ if( ![aString isKindOfClass:[NSAttributedString class]] )
+ aString = [[[NSAttributedString alloc] initWithString:aString] autorelease];
+ NSRange rangeToReplace = [self hasMarkedText] ? [self markedRange] : [self selectedRange];
+ if( rangeToReplace.location == NSNotFound )
+ {
+ mMarkedRange = NSMakeRange( selRange.location, [aString length] );
+ mSelectedRange = NSMakeRange( selRange.location, selRange.length );
+ }
+ else
+ {
+ mMarkedRange = NSMakeRange( rangeToReplace.location, [aString length] );
+ mSelectedRange = NSMakeRange( rangeToReplace.location + selRange.location, selRange.length );
+ }
+
+ int len = [aString length];
+ SalExtTextInputEvent aInputEvent;
+ aInputEvent.mnTime = mpFrame->mnLastEventTime;
+ aInputEvent.mnDeltaStart = 0;
+ aInputEvent.mbOnlyCursor = FALSE;
+ if( len > 0 ) {
+ NSString *pString = [aString string];
+ OUString aInsertString( GetOUString( pString ) );
+ std::vector<sal_uInt16> aInputFlags = std::vector<sal_uInt16>( std::max( 1, len ), 0 );
+ for ( int i = 0; i < len; i++ )
+ {
+ unsigned int nUnderlineValue;
+ NSRange effectiveRange;
+
+ effectiveRange = NSMakeRange(i, 1);
+ nUnderlineValue = [[aString attribute:NSUnderlineStyleAttributeName atIndex:i effectiveRange:&effectiveRange] unsignedIntValue];
+
+ switch (nUnderlineValue & 0xff) {
+ case NSUnderlineStyleSingle:
+ aInputFlags[i] = EXTTEXTINPUT_ATTR_UNDERLINE;
+ break;
+ case NSUnderlineStyleThick:
+ aInputFlags[i] = EXTTEXTINPUT_ATTR_UNDERLINE | EXTTEXTINPUT_ATTR_HIGHLIGHT;
+ break;
+ case NSUnderlineStyleDouble:
+ aInputFlags[i] = EXTTEXTINPUT_ATTR_BOLDUNDERLINE;
+ break;
+ default:
+ aInputFlags[i] = EXTTEXTINPUT_ATTR_HIGHLIGHT;
+ break;
+ }
+ }
+
+ aInputEvent.maText = aInsertString;
+ aInputEvent.mnCursorPos = selRange.location;
+ aInputEvent.mpTextAttr = &aInputFlags[0];
+ mpFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void *)&aInputEvent );
+ } else {
+ aInputEvent.maText = OUString();
+ aInputEvent.mnCursorPos = 0;
+ aInputEvent.mnCursorFlags = 0;
+ aInputEvent.mpTextAttr = 0;
+ mpFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void *)&aInputEvent );
+ mpFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, 0 );
+ }
+ mbKeyHandled= true;
+}
+
+- (void)unmarkText
+{
+ mSelectedRange = mMarkedRange = NSMakeRange(NSNotFound, 0);
+}
+
+- (NSAttributedString *)attributedSubstringFromRange:(NSRange)theRange
+{
+ (void)theRange;
+ // FIXME
+ return nil;
+}
+
+- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
+{
+ (void)thePoint;
+ // FIXME
+ return 0;
+}
+
+- (NSInteger)conversationIdentifier
+{
+ return (long)self;
+}
+
+- (void)doCommandBySelector:(SEL)aSelector
+{
+ if( AquaSalFrame::isAlive( mpFrame ) )
+ {
+ #if OSL_DEBUG_LEVEL > 1
+ // fprintf( stderr, "SalFrameView: doCommandBySelector %s\n", (char*)aSelector );
+ #endif
+ if( (mpFrame->mnICOptions & SAL_INPUTCONTEXT_TEXT) != 0 &&
+ aSelector != NULL && [self respondsToSelector: aSelector] )
+ {
+ [self performSelector: aSelector];
+ }
+ else
+ {
+ [self sendSingleCharacter:mpLastEvent];
+ }
+ }
+
+ mbKeyHandled = true;
+}
+
+-(void)clearLastEvent
+{
+ mpLastEvent = nil;
+}
+
+- (NSRect)firstRectForCharacterRange:(NSRange)theRange
+{
+ (void)theRange;
+ SalExtTextInputPosEvent aPosEvent;
+ mpFrame->CallCallback( SALEVENT_EXTTEXTINPUTPOS, (void *)&aPosEvent );
+
+ NSRect rect;
+
+ rect.origin.x = aPosEvent.mnX + mpFrame->maGeometry.nX;
+ rect.origin.y = aPosEvent.mnY + mpFrame->maGeometry.nY + 4; // add some space for underlines
+ rect.size.width = aPosEvent.mnWidth;
+ rect.size.height = aPosEvent.mnHeight;
+
+ mpFrame->VCLToCocoa( rect );
+ return rect;
+}
+
+-(id)parentAttribute {
+ return mpFrame->getNSWindow();
+}
+
+-(::com::sun::star::accessibility::XAccessibleContext *)accessibleContext
+{
+ if ( mpReferenceWrapper == nil ) {
+ // some frames never become visible ..
+ Window *pWindow = mpFrame -> GetWindow();
+ if ( ! pWindow )
+ return nil;
+
+ mpReferenceWrapper = new ReferenceWrapper;
+ mpReferenceWrapper -> rAccessibleContext = pWindow -> /*GetAccessibleChildWindow( 0 ) ->*/ GetAccessible() -> getAccessibleContext();
+ [ AquaA11yFactory insertIntoWrapperRepository: self forAccessibleContext: mpReferenceWrapper -> rAccessibleContext ];
+ }
+ return [ super accessibleContext ];
+}
+
+-(NSView*)viewElementForParent
+{
+ return mpFrame->getNSView();
+}
+
+-(void)registerMouseEventListener: (id)theListener
+{
+ mpMouseEventListener = theListener;
+}
+
+-(void)unregisterMouseEventListener: (id)theListener
+{
+ (void)theListener;
+ mpMouseEventListener = nil;
+}
+
+-(NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
+{
+ return [mDraggingDestinationHandler draggingEntered: sender];
+}
+
+-(NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
+{
+ return [mDraggingDestinationHandler draggingUpdated: sender];
+}
+
+-(void)draggingExited:(id <NSDraggingInfo>)sender
+{
+ [mDraggingDestinationHandler draggingExited: sender];
+}
+
+-(BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
+{
+ return [mDraggingDestinationHandler prepareForDragOperation: sender];
+}
+
+-(BOOL)performDragOperation:(id <NSDraggingInfo>)sender
+{
+ return [mDraggingDestinationHandler performDragOperation: sender];
+}
+
+-(void)concludeDragOperation:(id <NSDraggingInfo>)sender
+{
+ [mDraggingDestinationHandler concludeDragOperation: sender];
+}
+
+-(void)registerDraggingDestinationHandler:(id)theHandler
+{
+ mDraggingDestinationHandler = theHandler;
+}
+
+-(void)unregisterDraggingDestinationHandler:(id)theHandler
+{
+ (void)theHandler;
+ mDraggingDestinationHandler = nil;
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/salinst.cxx b/vcl/osx/salinst.cxx
new file mode 100644
index 000000000000..9aab3a066c24
--- /dev/null
+++ b/vcl/osx/salinst.cxx
@@ -0,0 +1,1207 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#include <stdio.h>
+
+#include <tools/solarmutex.hxx>
+
+#include "osl/process.h"
+
+#include "rtl/ustrbuf.hxx"
+
+#include "vcl/svapp.hxx"
+#include "vcl/window.hxx"
+#include "vcl/timer.hxx"
+#include "vcl/solarmutex.hxx"
+
+#include "osx/saldata.hxx"
+#include "osx/salinst.h"
+#include "osx/salframe.h"
+#include "osx/salobj.h"
+#include "osx/salsys.h"
+#include "osx/salvd.h"
+#include "quartz/salbmp.h"
+#include "quartz/utils.h"
+#include "osx/salprn.h"
+#include "osx/saltimer.h"
+#include "osx/vclnsapp.h"
+
+#include "print.h"
+#include "impbmp.hxx"
+#include "salimestatus.hxx"
+
+#include <comphelper/processfactory.hxx>
+
+#include <com/sun/star/uri/XExternalUriReferenceTranslator.hpp>
+#include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include "premac.h"
+#include <Foundation/Foundation.h>
+#include <ApplicationServices/ApplicationServices.h>
+#import "apple_remote/RemoteMainController.h"
+#include "apple_remote/RemoteControl.h"
+#include "postmac.h"
+
+using namespace std;
+using namespace ::com::sun::star;
+
+extern sal_Bool ImplSVMain();
+
+static int* gpnInit = 0;
+static NSMenu* pDockMenu = nil;
+static bool bNoSVMain = true;
+static bool bLeftMain = false;
+// -----------------------------------------------------------------------
+
+class AquaDelayedSettingsChanged : public Timer
+{
+ bool mbInvalidate;
+ public:
+ AquaDelayedSettingsChanged( bool bInvalidate ) :
+ mbInvalidate( bInvalidate )
+ {
+ }
+
+ virtual void Timeout()
+ {
+ SalData* pSalData = GetSalData();
+ if( ! pSalData->maFrames.empty() )
+ pSalData->maFrames.front()->CallCallback( SALEVENT_SETTINGSCHANGED, NULL );
+
+ if( mbInvalidate )
+ {
+ for( std::list< AquaSalFrame* >::iterator it = pSalData->maFrames.begin();
+ it != pSalData->maFrames.end(); ++it )
+ {
+ if( (*it)->mbShown )
+ (*it)->SendPaintEvent( NULL );
+ }
+ }
+ Stop();
+ delete this;
+ }
+};
+
+void AquaSalInstance::delayedSettingsChanged( bool bInvalidate )
+{
+ osl::Guard< comphelper::SolarMutex > aGuard( *mpSalYieldMutex );
+ AquaDelayedSettingsChanged* pTimer = new AquaDelayedSettingsChanged( bInvalidate );
+ pTimer->SetTimeout( 50 );
+ pTimer->Start();
+}
+
+
+// the AppEventList must be available before any SalData/SalInst/etc. objects are ready
+AquaSalInstance::AppEventList AquaSalInstance::aAppEventList;
+
+NSMenu* AquaSalInstance::GetDynamicDockMenu()
+{
+ if( ! pDockMenu && ! bLeftMain )
+ pDockMenu = [[NSMenu alloc] initWithTitle: @""];
+ return pDockMenu;
+}
+
+bool AquaSalInstance::isOnCommandLine( const OUString& rArg )
+{
+ sal_uInt32 nArgs = osl_getCommandArgCount();
+ for( sal_uInt32 i = 0; i < nArgs; i++ )
+ {
+ OUString aArg;
+ osl_getCommandArg( i, &aArg.pData );
+ if( aArg.equals( rArg ) )
+ return true;
+ }
+ return false;
+}
+
+
+// initialize the cocoa VCL_NSApplication object
+// returns an NSAutoreleasePool that must be released when the event loop begins
+static void initNSApp()
+{
+ // create our cocoa NSApplication
+ [VCL_NSApplication sharedApplication];
+
+ SalData::ensureThreadAutoreleasePool();
+
+ // put cocoa into multithreaded mode
+ [NSThread detachNewThreadSelector:@selector(enableCocoaThreads:) toTarget:[[CocoaThreadEnabler alloc] init] withObject:nil];
+
+ // activate our delegate methods
+ [NSApp setDelegate: NSApp];
+
+ [[NSNotificationCenter defaultCenter] addObserver: NSApp
+ selector: @selector(systemColorsChanged:)
+ name: NSSystemColorsDidChangeNotification
+ object: nil ];
+ [[NSNotificationCenter defaultCenter] addObserver: NSApp
+ selector: @selector(screenParametersChanged:)
+ name: NSApplicationDidChangeScreenParametersNotification
+ object: nil ];
+ // add observers for some settings changes that affect vcl's settings
+ // scrollbar variant
+ [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
+ selector: @selector(scrollbarVariantChanged:)
+ name: @"AppleAquaScrollBarVariantChanged"
+ object: nil ];
+ // scrollbar page behavior ("jump to here" or not)
+ [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
+ selector: @selector(scrollbarSettingsChanged:)
+ name: @"AppleNoRedisplayAppearancePreferenceChanged"
+ object: nil ];
+#if !HAVE_FEATURE_MACOSX_SANDBOX
+ // Initialize Apple Remote
+ GetSalData()->mpMainController = [[MainController alloc] init];
+
+ [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
+ selector: @selector(applicationWillBecomeActive:)
+ name: @"AppleRemoteWillBecomeActive"
+ object: nil ];
+
+ [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
+ selector: @selector(applicationWillResignActive:)
+ name: @"AppleRemoteWillResignActive"
+ object: nil ];
+#endif
+}
+
+sal_Bool ImplSVMainHook( int * pnInit )
+{
+ unlink([[NSString stringWithFormat:@"%@/Library/Saved Application State/%s.savedState/restorecount.plist", NSHomeDirectory(), MACOSX_BUNDLE_IDENTIFIER] UTF8String]);
+
+ gpnInit = pnInit;
+
+ bNoSVMain = false;
+ initNSApp();
+
+ NSPoint aPt = { 0, 0 };
+ NSEvent* pEvent = [NSEvent otherEventWithType: NSApplicationDefined
+ location: aPt
+ modifierFlags: 0
+ timestamp: 0
+ windowNumber: 0
+ context: nil
+ subtype: AquaSalInstance::AppExecuteSVMain
+ data1: 0
+ data2: 0 ];
+ if( pEvent )
+ {
+ [NSApp postEvent: pEvent atStart: NO];
+
+ OUString aExeURL, aExe;
+ osl_getExecutableFile( &aExeURL.pData );
+ osl_getSystemPathFromFileURL( aExeURL.pData, &aExe.pData );
+ OString aByteExe( OUStringToOString( aExe, osl_getThreadTextEncoding() ) );
+
+#ifdef DEBUG
+ aByteExe += OString ( " NSAccessibilityDebugLogLevel 1" );
+ const char* pArgv[] = { aByteExe.getStr(), NULL };
+ NSApplicationMain( 3, pArgv );
+#else
+ const char* pArgv[] = { aByteExe.getStr(), NULL };
+ NSApplicationMain( 1, pArgv );
+#endif
+ }
+ else
+ {
+ OSL_FAIL( "NSApplication initialization could not be done" );
+ }
+
+ return TRUE; // indicate that ImplSVMainHook is implemented
+}
+
+// =======================================================================
+
+void SalAbort( const OUString& rErrorText, bool bDumpCore )
+{
+ if( rErrorText.isEmpty() )
+ fprintf( stderr, "Application Error " );
+ else
+ fprintf( stderr, "%s ",
+ OUStringToOString( rErrorText, osl_getThreadTextEncoding() ).getStr() );
+ if( bDumpCore )
+ abort();
+ else
+ _exit(1);
+}
+
+// -----------------------------------------------------------------------
+
+void InitSalData()
+{
+ SalData *pSalData = new SalData;
+ SetSalData( pSalData );
+}
+
+// -----------------------------------------------------------------------
+
+const OUString& SalGetDesktopEnvironment()
+{
+ static OUString aDesktopEnvironment( "MacOSX" );
+ return aDesktopEnvironment;
+}
+
+// -----------------------------------------------------------------------
+
+void DeInitSalData()
+{
+ SalData *pSalData = GetSalData();
+ if( pSalData->mpStatusItem )
+ {
+ [pSalData->mpStatusItem release];
+ pSalData->mpStatusItem = nil;
+ }
+ delete pSalData;
+ SetSalData( NULL );
+}
+
+// -----------------------------------------------------------------------
+
+extern "C" {
+#include <crt_externs.h>
+}
+
+// -----------------------------------------------------------------------
+
+void InitSalMain()
+{
+}
+
+// =======================================================================
+
+SalYieldMutex::SalYieldMutex()
+{
+ mnCount = 0;
+ mnThreadId = 0;
+}
+
+void SalYieldMutex::acquire()
+{
+ SolarMutexObject::acquire();
+ mnThreadId = osl::Thread::getCurrentIdentifier();
+ mnCount++;
+}
+
+void SalYieldMutex::release()
+{
+ if ( mnThreadId == osl::Thread::getCurrentIdentifier() )
+ {
+ if ( mnCount == 1 )
+ mnThreadId = 0;
+ mnCount--;
+ }
+ SolarMutexObject::release();
+}
+
+bool SalYieldMutex::tryToAcquire()
+{
+ if ( SolarMutexObject::tryToAcquire() )
+ {
+ mnThreadId = osl::Thread::getCurrentIdentifier();
+ mnCount++;
+ return true;
+ }
+ else
+ return false;
+}
+
+// -----------------------------------------------------------------------
+
+// some convenience functions regarding the yield mutex, aka solar mutex
+
+sal_Bool ImplSalYieldMutexTryToAcquire()
+{
+ AquaSalInstance* pInst = (AquaSalInstance*) GetSalData()->mpFirstInstance;
+ if ( pInst )
+ return pInst->mpSalYieldMutex->tryToAcquire();
+ else
+ return FALSE;
+}
+
+void ImplSalYieldMutexAcquire()
+{
+ AquaSalInstance* pInst = (AquaSalInstance*) GetSalData()->mpFirstInstance;
+ if ( pInst )
+ pInst->mpSalYieldMutex->acquire();
+}
+
+void ImplSalYieldMutexRelease()
+{
+ AquaSalInstance* pInst = (AquaSalInstance*) GetSalData()->mpFirstInstance;
+ if ( pInst )
+ pInst->mpSalYieldMutex->release();
+}
+
+// =======================================================================
+
+SalInstance* CreateSalInstance()
+{
+ // this is the case for not using SVMain
+ // not so good
+ if( bNoSVMain )
+ initNSApp();
+
+ SalData* pSalData = GetSalData();
+ DBG_ASSERT( pSalData->mpFirstInstance == NULL, "more than one instance created" );
+ AquaSalInstance* pInst = new AquaSalInstance;
+
+ // init instance (only one instance in this version !!!)
+ pSalData->mpFirstInstance = pInst;
+ // this one is for outside AquaSalInstance::Yield
+ SalData::ensureThreadAutoreleasePool();
+ // no focus rects on NWF
+ ImplGetSVData()->maNWFData.mbNoFocusRects = true;
+ ImplGetSVData()->maNWFData.mbNoActiveTabTextRaise = true;
+ ImplGetSVData()->maNWFData.mbCenteredTabs = true;
+ ImplGetSVData()->maNWFData.mbProgressNeedsErase = true;
+ ImplGetSVData()->maNWFData.mbCheckBoxNeedsErase = true;
+ ImplGetSVData()->maNWFData.mnStatusBarLowerRightOffset = 10;
+ ImplGetSVData()->maGDIData.mbNoXORClipping = true;
+ ImplGetSVData()->maWinData.mbNoSaveBackground = true;
+
+ return pInst;
+}
+
+// -----------------------------------------------------------------------
+
+void DestroySalInstance( SalInstance* pInst )
+{
+ delete pInst;
+}
+
+// -----------------------------------------------------------------------
+
+AquaSalInstance::AquaSalInstance()
+{
+ mpSalYieldMutex = new SalYieldMutex;
+ mpSalYieldMutex->acquire();
+ ::tools::SolarMutex::SetSolarMutex( mpSalYieldMutex );
+ maMainThread = osl::Thread::getCurrentIdentifier();
+ mbWaitingYield = false;
+ maUserEventListMutex = osl_createMutex();
+ mnActivePrintJobs = 0;
+ maWaitingYieldCond = osl_createCondition();
+}
+
+// -----------------------------------------------------------------------
+
+AquaSalInstance::~AquaSalInstance()
+{
+ ::tools::SolarMutex::SetSolarMutex( 0 );
+ mpSalYieldMutex->release();
+ delete mpSalYieldMutex;
+ osl_destroyMutex( maUserEventListMutex );
+ osl_destroyCondition( maWaitingYieldCond );
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalInstance::wakeupYield()
+{
+ // wakeup :Yield
+ if( mbWaitingYield )
+ {
+ SalData::ensureThreadAutoreleasePool();
+ NSPoint aPt = { 0, 0 };
+ NSEvent* pEvent = [NSEvent otherEventWithType: NSApplicationDefined
+ location: aPt
+ modifierFlags: 0
+ timestamp: 0
+ windowNumber: 0
+ context: nil
+ subtype: AquaSalInstance::YieldWakeupEvent
+ data1: 0
+ data2: 0 ];
+ if( pEvent )
+ [NSApp postEvent: pEvent atStart: NO];
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalInstance::PostUserEvent( AquaSalFrame* pFrame, sal_uInt16 nType, void* pData )
+{
+ osl_acquireMutex( maUserEventListMutex );
+ maUserEvents.push_back( SalUserEvent( pFrame, pData, nType ) );
+ osl_releaseMutex( maUserEventListMutex );
+
+ // notify main loop that an event has arrived
+ wakeupYield();
+}
+
+// -----------------------------------------------------------------------
+
+comphelper::SolarMutex* AquaSalInstance::GetYieldMutex()
+{
+ return mpSalYieldMutex;
+}
+
+// -----------------------------------------------------------------------
+
+sal_uLong AquaSalInstance::ReleaseYieldMutex()
+{
+ SalYieldMutex* pYieldMutex = mpSalYieldMutex;
+ if ( pYieldMutex->GetThreadId() ==
+ osl::Thread::getCurrentIdentifier() )
+ {
+ sal_uLong nCount = pYieldMutex->GetAcquireCount();
+ sal_uLong n = nCount;
+ while ( n )
+ {
+ pYieldMutex->release();
+ n--;
+ }
+
+ return nCount;
+ }
+ else
+ return 0;
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalInstance::AcquireYieldMutex( sal_uLong nCount )
+{
+ SalYieldMutex* pYieldMutex = mpSalYieldMutex;
+ while ( nCount )
+ {
+ pYieldMutex->acquire();
+ nCount--;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+bool AquaSalInstance::CheckYieldMutex()
+{
+ bool bRet = true;
+
+ SalYieldMutex* pYieldMutex = mpSalYieldMutex;
+ if ( pYieldMutex->GetThreadId() != osl::Thread::getCurrentIdentifier())
+ {
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+// -----------------------------------------------------------------------
+
+bool AquaSalInstance::isNSAppThread() const
+{
+ return osl::Thread::getCurrentIdentifier() == maMainThread;
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalInstance::handleAppDefinedEvent( NSEvent* pEvent )
+{
+ switch( [pEvent subtype] )
+ {
+ case AppStartTimerEvent:
+ AquaSalTimer::handleStartTimerEvent( pEvent );
+ break;
+ case AppEndLoopEvent:
+ [NSApp stop: NSApp];
+ break;
+ case AppExecuteSVMain:
+ {
+ int nResult = ImplSVMain();
+ if( gpnInit )
+ *gpnInit = nResult;
+ [NSApp stop: NSApp];
+ bLeftMain = true;
+ if( pDockMenu )
+ {
+ [pDockMenu release];
+ pDockMenu = nil;
+ }
+ }
+ break;
+ case AppleRemoteEvent:
+ {
+ sal_Int16 nCommand = 0;
+ SalData* pSalData = GetSalData();
+ bool bIsFullScreenMode = false;
+
+ std::list<AquaSalFrame*>::iterator it = pSalData->maFrames.begin();
+ while( it != pSalData->maFrames.end() )
+ {
+ if ( (*it) && ((*it)->mbFullScreen == true) )
+ bIsFullScreenMode = true;
+ ++it;
+ }
+
+ switch ([pEvent data1])
+ {
+ case kRemoteButtonPlay:
+ nCommand = ( bIsFullScreenMode == true ) ? MEDIA_COMMAND_PLAY_PAUSE : MEDIA_COMMAND_PLAY;
+ break;
+
+ // kept for experimentation purpose (scheduled for future implementation)
+ // case kRemoteButtonMenu: nCommand = MEDIA_COMMAND_MENU; break;
+
+ case kRemoteButtonPlus: nCommand = MEDIA_COMMAND_VOLUME_UP; break;
+
+ case kRemoteButtonMinus: nCommand = MEDIA_COMMAND_VOLUME_DOWN; break;
+
+ case kRemoteButtonRight: nCommand = MEDIA_COMMAND_NEXTTRACK; break;
+
+ case kRemoteButtonRight_Hold: nCommand = MEDIA_COMMAND_NEXTTRACK_HOLD; break;
+
+ case kRemoteButtonLeft: nCommand = MEDIA_COMMAND_PREVIOUSTRACK; break;
+
+ case kRemoteButtonLeft_Hold: nCommand = MEDIA_COMMAND_REWIND; break;
+
+ case kRemoteButtonPlay_Hold: nCommand = MEDIA_COMMAND_PLAY_HOLD; break;
+
+ case kRemoteButtonMenu_Hold: nCommand = MEDIA_COMMAND_STOP; break;
+
+ // FIXME : not detected
+ case kRemoteButtonPlus_Hold:
+ case kRemoteButtonMinus_Hold:
+ break;
+
+ default:
+ break;
+ }
+ AquaSalFrame* pFrame = pSalData->maFrames.front();
+ Window * pWindow = pFrame->GetWindow() ? pSalData->maFrames.front()->GetWindow() : NULL;
+
+ if( pWindow )
+ {
+ const Point aPoint;
+ CommandEvent aCEvt( aPoint, COMMAND_MEDIA, FALSE, &nCommand );
+ NotifyEvent aNCmdEvt( EVENT_COMMAND, pWindow, &aCEvt );
+
+ if ( !ImplCallPreNotify( aNCmdEvt ) )
+ pWindow->Command( aCEvt );
+ }
+
+ }
+ break;
+
+ case YieldWakeupEvent:
+ // do nothing, fall out of Yield
+ break;
+
+ default:
+ OSL_FAIL( "unhandled NSApplicationDefined event" );
+ break;
+ };
+}
+
+// -----------------------------------------------------------------------
+
+class ReleasePoolHolder
+{
+ NSAutoreleasePool* mpPool;
+ public:
+ ReleasePoolHolder() : mpPool( [[NSAutoreleasePool alloc] init] ) {}
+ ~ReleasePoolHolder() { [mpPool release]; }
+};
+
+void AquaSalInstance::Yield( bool bWait, bool bHandleAllCurrentEvents )
+{
+ // ensure that the per thread autorelease pool is top level and
+ // will therefore not be destroyed by cocoa implicitly
+ SalData::ensureThreadAutoreleasePool();
+
+ // NSAutoreleasePool documentation suggests we should have
+ // an own pool for each yield level
+ ReleasePoolHolder aReleasePool;
+
+ // Release all locks so that we don't deadlock when we pull pending
+ // events from the event queue
+ bool bDispatchUser = true;
+ while( bDispatchUser )
+ {
+ sal_uLong nCount = ReleaseYieldMutex();
+
+ // get one user event
+ osl_acquireMutex( maUserEventListMutex );
+ SalUserEvent aEvent( NULL, NULL, 0 );
+ if( ! maUserEvents.empty() )
+ {
+ aEvent = maUserEvents.front();
+ maUserEvents.pop_front();
+ }
+ else
+ bDispatchUser = false;
+ osl_releaseMutex( maUserEventListMutex );
+
+ AcquireYieldMutex( nCount );
+
+ // dispatch it
+ if( aEvent.mpFrame && AquaSalFrame::isAlive( aEvent.mpFrame ) )
+ {
+ aEvent.mpFrame->CallCallback( aEvent.mnType, aEvent.mpData );
+ osl_setCondition( maWaitingYieldCond );
+ // return if only one event is asked for
+ if( ! bHandleAllCurrentEvents )
+ return;
+ }
+ }
+
+ // handle cocoa event queue
+ // cocoa events mye be only handled in the thread the NSApp was created
+ if( isNSAppThread() && mnActivePrintJobs == 0 )
+ {
+ // we need to be woken up by a cocoa-event
+ // if a user event should be posted by the event handling below
+ bool bOldWaitingYield = mbWaitingYield;
+ mbWaitingYield = bWait;
+
+ // handle available events
+ NSEvent* pEvent = nil;
+ bool bHadEvent = false;
+ do
+ {
+ sal_uLong nCount = ReleaseYieldMutex();
+
+ pEvent = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: nil
+ inMode: NSDefaultRunLoopMode dequeue: YES];
+ if( pEvent )
+ {
+ [NSApp sendEvent: pEvent];
+ bHadEvent = true;
+ }
+ [NSApp updateWindows];
+
+ AcquireYieldMutex( nCount );
+ } while( bHandleAllCurrentEvents && pEvent );
+
+ // if we had no event yet, wait for one if requested
+ if( bWait && ! bHadEvent )
+ {
+ sal_uLong nCount = ReleaseYieldMutex();
+
+ NSDate* pDt = AquaSalTimer::pRunningTimer ? [AquaSalTimer::pRunningTimer fireDate] : [NSDate distantFuture];
+ pEvent = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: pDt
+ inMode: NSDefaultRunLoopMode dequeue: YES];
+ if( pEvent )
+ [NSApp sendEvent: pEvent];
+ [NSApp updateWindows];
+
+ AcquireYieldMutex( nCount );
+
+ // #i86581#
+ // FIXME: sometimes the NSTimer will never fire. Firing it by hand then
+ // fixes the problem even seems to set the correct next firing date
+ // Why oh why ?
+ if( ! pEvent && AquaSalTimer::pRunningTimer )
+ {
+ // this cause crashes on MacOSX 10.4
+ // [AquaSalTimer::pRunningTimer fire];
+ ImplGetSVData()->mpSalTimer->CallCallback();
+ }
+ }
+
+ mbWaitingYield = bOldWaitingYield;
+
+ // collect update rectangles
+ const std::list< AquaSalFrame* > rFrames( GetSalData()->maFrames );
+ for( std::list< AquaSalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it )
+ {
+ if( (*it)->mbShown && ! (*it)->maInvalidRect.IsEmpty() )
+ {
+ (*it)->Flush( (*it)->maInvalidRect );
+ (*it)->maInvalidRect.SetEmpty();
+ }
+ }
+ osl_setCondition( maWaitingYieldCond );
+ }
+ else if( bWait )
+ {
+ // #i103162#
+ // wait until any thread (most likely the main thread)
+ // has dispatched an event, cop out at 200 ms
+ osl_resetCondition( maWaitingYieldCond );
+ TimeValue aVal = { 0, 200000000 };
+ sal_uLong nCount = ReleaseYieldMutex();
+ osl_waitCondition( maWaitingYieldCond, &aVal );
+ AcquireYieldMutex( nCount );
+ }
+
+ // we get some apple events way too early
+ // before the application is ready to handle them,
+ // so their corresponding application events need to be delayed
+ // now is a good time to handle at least one of them
+ if( bWait && !aAppEventList.empty() && ImplGetSVData()->maAppData.mbInAppExecute )
+ {
+ // make sure that only one application event is active at a time
+ static bool bInAppEvent = false;
+ if( !bInAppEvent )
+ {
+ bInAppEvent = true;
+ // get the next delayed application event
+ const ApplicationEvent* pAppEvent = aAppEventList.front();
+ aAppEventList.pop_front();
+ // handle one application event (no recursion)
+ const ImplSVData* pSVData = ImplGetSVData();
+ pSVData->mpApp->AppEvent( *pAppEvent );
+ delete pAppEvent;
+ // allow the next delayed application event
+ bInAppEvent = false;
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+bool AquaSalInstance::AnyInput( sal_uInt16 nType )
+{
+ if( nType & VCL_INPUT_APPEVENT )
+ {
+ if( ! aAppEventList.empty() )
+ return true;
+ if( nType == VCL_INPUT_APPEVENT )
+ return false;
+ }
+
+ if( nType & VCL_INPUT_TIMER )
+ {
+ if( AquaSalTimer::pRunningTimer )
+ {
+ NSDate* pDt = [AquaSalTimer::pRunningTimer fireDate];
+ if( pDt && [pDt timeIntervalSinceNow] < 0 )
+ {
+ return true;
+ }
+ }
+ }
+
+ unsigned/*NSUInteger*/ nEventMask = 0;
+ if( nType & VCL_INPUT_MOUSE)
+ nEventMask |=
+ NSLeftMouseDownMask | NSRightMouseDownMask | NSOtherMouseDownMask |
+ NSLeftMouseUpMask | NSRightMouseUpMask | NSOtherMouseUpMask |
+ NSLeftMouseDraggedMask | NSRightMouseDraggedMask | NSOtherMouseDraggedMask |
+ NSScrollWheelMask |
+ // NSMouseMovedMask |
+ NSMouseEnteredMask | NSMouseExitedMask;
+ if( nType & VCL_INPUT_KEYBOARD)
+ nEventMask |= NSKeyDownMask | NSKeyUpMask | NSFlagsChangedMask;
+ if( nType & VCL_INPUT_OTHER)
+ nEventMask |= NSTabletPoint;
+ // TODO: VCL_INPUT_PAINT / more VCL_INPUT_OTHER
+ if( !nType)
+ return false;
+
+ NSEvent* pEvent = [NSApp nextEventMatchingMask: nEventMask untilDate: nil
+ inMode: NSDefaultRunLoopMode dequeue: NO];
+ return (pEvent != NULL);
+}
+
+// -----------------------------------------------------------------------
+
+SalFrame* AquaSalInstance::CreateChildFrame( SystemParentData*, sal_uLong /*nSalFrameStyle*/ )
+{
+ return NULL;
+}
+
+// -----------------------------------------------------------------------
+
+SalFrame* AquaSalInstance::CreateFrame( SalFrame* pParent, sal_uLong nSalFrameStyle )
+{
+ SalData::ensureThreadAutoreleasePool();
+
+ SalFrame* pFrame = new AquaSalFrame( pParent, nSalFrameStyle );
+ return pFrame;
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalInstance::DestroyFrame( SalFrame* pFrame )
+{
+ delete pFrame;
+}
+
+// -----------------------------------------------------------------------
+
+SalObject* AquaSalInstance::CreateObject( SalFrame* pParent, SystemWindowData* /* pWindowData */, sal_Bool /* bShow */ )
+{
+ // SystemWindowData is meaningless on Mac OS X
+ AquaSalObject *pObject = NULL;
+
+ if ( pParent )
+ pObject = new AquaSalObject( static_cast<AquaSalFrame*>(pParent) );
+
+ return pObject;
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalInstance::DestroyObject( SalObject* pObject )
+{
+ delete ( pObject );
+}
+
+// -----------------------------------------------------------------------
+
+SalPrinter* AquaSalInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter )
+{
+ return new AquaSalPrinter( dynamic_cast<AquaSalInfoPrinter*>(pInfoPrinter) );
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalInstance::DestroyPrinter( SalPrinter* pPrinter )
+{
+ delete pPrinter;
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList )
+{
+ NSArray* pNames = [NSPrinter printerNames];
+ NSArray* pTypes = [NSPrinter printerTypes];
+ unsigned int nNameCount = pNames ? [pNames count] : 0;
+ unsigned int nTypeCount = pTypes ? [pTypes count] : 0;
+ DBG_ASSERT( nTypeCount == nNameCount, "type count not equal to printer count" );
+ for( unsigned int i = 0; i < nNameCount; i++ )
+ {
+ NSString* pName = [pNames objectAtIndex: i];
+ NSString* pType = i < nTypeCount ? [pTypes objectAtIndex: i] : nil;
+ if( pName )
+ {
+ SalPrinterQueueInfo* pInfo = new SalPrinterQueueInfo;
+ pInfo->maPrinterName = GetOUString( pName );
+ if( pType )
+ pInfo->maDriver = GetOUString( pType );
+ pInfo->mnStatus = 0;
+ pInfo->mnJobs = 0;
+ pInfo->mpSysData = NULL;
+
+ pList->Add( pInfo );
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalInstance::GetPrinterQueueState( SalPrinterQueueInfo* )
+{
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalInstance::DeletePrinterQueueInfo( SalPrinterQueueInfo* pInfo )
+{
+ delete pInfo;
+}
+
+// -----------------------------------------------------------------------
+
+OUString AquaSalInstance::GetDefaultPrinter()
+{
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ if( maDefaultPrinter.isEmpty() )
+ {
+ NSPrintInfo* pPI = [NSPrintInfo sharedPrintInfo];
+ DBG_ASSERT( pPI, "no print info" );
+ if( pPI )
+ {
+ NSPrinter* pPr = [pPI printer];
+ DBG_ASSERT( pPr, "no printer in default info" );
+ if( pPr )
+ {
+ NSString* pDefName = [pPr name];
+ DBG_ASSERT( pDefName, "printer has no name" );
+ maDefaultPrinter = GetOUString( pDefName );
+ }
+ }
+ }
+ return maDefaultPrinter;
+}
+
+// -----------------------------------------------------------------------
+
+SalInfoPrinter* AquaSalInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo,
+ ImplJobSetup* pSetupData )
+{
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ SalInfoPrinter* pNewInfoPrinter = NULL;
+ if( pQueueInfo )
+ {
+ pNewInfoPrinter = new AquaSalInfoPrinter( *pQueueInfo );
+ if( pSetupData )
+ pNewInfoPrinter->SetPrinterData( pSetupData );
+ }
+
+ return pNewInfoPrinter;
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter )
+{
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ delete pPrinter;
+}
+
+// -----------------------------------------------------------------------
+
+SalSystem* AquaSalInstance::CreateSystem()
+{
+ return new AquaSalSystem();
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalInstance::DestroySystem( SalSystem* pSystem )
+{
+ delete pSystem;
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalInstance::SetEventCallback( void*, bool(*)(void*,void*,int) )
+{
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalInstance::SetErrorEventCallback( void*, bool(*)(void*,void*,int) )
+{
+}
+
+// -----------------------------------------------------------------------
+
+void* AquaSalInstance::GetConnectionIdentifier( ConnectionIdentifierType& rReturnedType, int& rReturnedBytes )
+{
+ rReturnedBytes = 1;
+ rReturnedType = AsciiCString;
+ return (void*)"";
+}
+
+// We need to re-encode file urls because osl_getFileURLFromSystemPath converts
+// to UTF-8 before encoding non ascii characters, which is not what other apps expect.
+static OUString translateToExternalUrl(const OUString& internalUrl)
+{
+ uno::Reference< uno::XComponentContext > context(
+ comphelper::getProcessComponentContext());
+ return uri::ExternalUriReferenceTranslator::create(context)->translateToExternal(internalUrl);
+}
+
+// #i104525# many versions of OSX have problems with some URLs:
+// when an app requests OSX to add one of these URLs to the "Recent Items" list
+// then this app gets killed (TextEdit, Preview, etc. and also OOo)
+static bool isDangerousUrl( const OUString& rUrl )
+{
+ // use a heuristic that detects all known cases since there is no official comment
+ // on the exact impact and root cause of the OSX bug
+ const int nLen = rUrl.getLength();
+ const sal_Unicode* p = rUrl.getStr();
+ for( int i = 0; i < nLen-3; ++i, ++p ) {
+ if( p[0] != '%' )
+ continue;
+ // escaped percent?
+ if( (p[1] == '2') && (p[2] == '5') )
+ return true;
+ // escapes are considered to be UTF-8 encoded
+ // => check for invalid UTF-8 leading byte
+ if( (p[1] != 'f') && (p[1] != 'F') )
+ continue;
+ int cLowNibble = p[2];
+ if( (cLowNibble >= '0' ) && (cLowNibble <= '9'))
+ return false;
+ if( cLowNibble >= 'a' )
+ cLowNibble -= 'a' - 'A';
+ if( (cLowNibble < 'A') || (cLowNibble >= 'C'))
+ return true;
+ }
+
+ return false;
+}
+
+void AquaSalInstance::AddToRecentDocumentList(const OUString& rFileUrl, const OUString& /*rMimeType*/, const OUString& /*rDocumentService*/)
+{
+ // Convert file URL for external use (see above)
+ OUString externalUrl = translateToExternalUrl(rFileUrl);
+ if( externalUrl.isEmpty() )
+ externalUrl = rFileUrl;
+
+ if( !externalUrl.isEmpty() && !isDangerousUrl( externalUrl ) )
+ {
+ NSString* pString = CreateNSString( externalUrl );
+ NSURL* pURL = [NSURL URLWithString: pString];
+
+ if( pURL )
+ {
+ NSDocumentController* pCtrl = [NSDocumentController sharedDocumentController];
+ [pCtrl noteNewRecentDocumentURL: pURL];
+ }
+ if( pString )
+ [pString release];
+ }
+}
+
+
+// -----------------------------------------------------------------------
+
+SalTimer* AquaSalInstance::CreateSalTimer()
+{
+ return new AquaSalTimer();
+}
+
+// -----------------------------------------------------------------------
+
+SalSystem* AquaSalInstance::CreateSalSystem()
+{
+ return new AquaSalSystem();
+}
+
+// -----------------------------------------------------------------------
+
+SalBitmap* AquaSalInstance::CreateSalBitmap()
+{
+ return new QuartzSalBitmap();
+}
+
+// -----------------------------------------------------------------------
+
+SalSession* AquaSalInstance::CreateSalSession()
+{
+ return NULL;
+}
+
+// -----------------------------------------------------------------------
+
+class MacImeStatus : public SalI18NImeStatus
+{
+public:
+ MacImeStatus() {}
+ virtual ~MacImeStatus() {}
+
+ // asks whether there is a status window available
+ // to toggle into menubar
+ virtual bool canToggle() { return false; }
+ virtual void toggle() {}
+};
+
+// -----------------------------------------------------------------------
+
+SalI18NImeStatus* AquaSalInstance::CreateI18NImeStatus()
+{
+ return new MacImeStatus();
+}
+
+// YieldMutexReleaser
+YieldMutexReleaser::YieldMutexReleaser() : mnCount( 0 )
+{
+ SalData* pSalData = GetSalData();
+ if( ! pSalData->mpFirstInstance->isNSAppThread() )
+ {
+ SalData::ensureThreadAutoreleasePool();
+ mnCount = pSalData->mpFirstInstance->ReleaseYieldMutex();
+ }
+}
+
+YieldMutexReleaser::~YieldMutexReleaser()
+{
+ if( mnCount != 0 )
+ GetSalData()->mpFirstInstance->AcquireYieldMutex( mnCount );
+}
+
+CGImageRef CreateCGImage( const Image& rImage )
+{
+ BitmapEx aBmpEx( rImage.GetBitmapEx() );
+ Bitmap aBmp( aBmpEx.GetBitmap() );
+
+ if( ! aBmp || ! aBmp.ImplGetImpBitmap() )
+ return NULL;
+
+ // simple case, no transparency
+ QuartzSalBitmap* pSalBmp = static_cast<QuartzSalBitmap*>(aBmp.ImplGetImpBitmap()->ImplGetSalBitmap());
+
+ if( ! pSalBmp )
+ return NULL;
+
+ CGImageRef xImage = NULL;
+ if( ! (aBmpEx.IsAlpha() || aBmpEx.IsTransparent() ) )
+ xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
+ else if( aBmpEx.IsAlpha() )
+ {
+ AlphaMask aAlphaMask( aBmpEx.GetAlpha() );
+ Bitmap aMask( aAlphaMask.GetBitmap() );
+ QuartzSalBitmap* pMaskBmp = static_cast<QuartzSalBitmap*>(aMask.ImplGetImpBitmap()->ImplGetSalBitmap());
+ if( pMaskBmp )
+ xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
+ else
+ xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
+ }
+ else if( aBmpEx.GetTransparentType() == TRANSPARENT_BITMAP )
+ {
+ Bitmap aMask( aBmpEx.GetMask() );
+ QuartzSalBitmap* pMaskBmp = static_cast<QuartzSalBitmap*>(aMask.ImplGetImpBitmap()->ImplGetSalBitmap());
+ if( pMaskBmp )
+ xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
+ else
+ xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
+ }
+ else if( aBmpEx.GetTransparentType() == TRANSPARENT_COLOR )
+ {
+ Color aTransColor( aBmpEx.GetTransparentColor() );
+ SalColor nTransColor = MAKE_SALCOLOR( aTransColor.GetRed(), aTransColor.GetGreen(), aTransColor.GetBlue() );
+ xImage = pSalBmp->CreateColorMask( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight, nTransColor );
+ }
+
+ return xImage;
+}
+
+NSImage* CreateNSImage( const Image& rImage )
+{
+ CGImageRef xImage = CreateCGImage( rImage );
+
+ if( ! xImage )
+ return nil;
+
+ Size aSize( rImage.GetSizePixel() );
+ NSImage* pImage = [[NSImage alloc] initWithSize: NSMakeSize( aSize.Width(), aSize.Height() )];
+ if( pImage )
+ {
+ [pImage setFlipped: YES];
+ [pImage lockFocus];
+
+ NSGraphicsContext* pContext = [NSGraphicsContext currentContext];
+ CGContextRef rCGContext = reinterpret_cast<CGContextRef>([pContext graphicsPort]);
+
+ const CGRect aDstRect = { {0, 0}, { static_cast<CGFloat>(aSize.Width()), static_cast<CGFloat>(aSize.Height()) } };
+ CGContextDrawImage( rCGContext, aDstRect, xImage );
+
+ [pImage unlockFocus];
+ }
+
+ CGImageRelease( xImage );
+
+ return pImage;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/salmenu.cxx b/vcl/osx/salmenu.cxx
new file mode 100644
index 000000000000..db3dd2ef845c
--- /dev/null
+++ b/vcl/osx/salmenu.cxx
@@ -0,0 +1,963 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/string.hxx>
+
+#include "rtl/ustrbuf.hxx"
+
+#include "vcl/cmdevt.hxx"
+#include "vcl/floatwin.hxx"
+#include "vcl/window.hxx"
+#include "vcl/svapp.hxx"
+
+#include "osx/saldata.hxx"
+#include "osx/salinst.h"
+#include "osx/salmenu.h"
+#include "osx/salnsmenu.h"
+#include "osx/salframe.h"
+#include "osx/a11ywrapper.h"
+#include "quartz/utils.h"
+
+#include "svids.hrc"
+#include "window.h"
+
+#include <objc/objc-runtime.h>
+
+const AquaSalMenu* AquaSalMenu::pCurrentMenuBar = NULL;
+
+@interface MainMenuSelector : NSObject
+{
+}
+-(void)showDialog: (int)nDialog;
+-(void)showPreferences: (id)sender;
+-(void)showAbout: (id)sender;
+@end
+
+@implementation MainMenuSelector
+-(void)showDialog: (int)nDialog
+{
+ if( AquaSalMenu::pCurrentMenuBar )
+ {
+ const AquaSalFrame* pFrame = AquaSalMenu::pCurrentMenuBar->mpFrame;
+ if( pFrame && AquaSalFrame::isAlive( pFrame ) )
+ {
+ pFrame->CallCallback( SALEVENT_SHOWDIALOG, reinterpret_cast<void*>(nDialog) );
+ }
+ }
+ else
+ {
+ OUString aDialog;
+ if( nDialog == SHOWDIALOG_ID_ABOUT )
+ aDialog = "ABOUT";
+ else if( nDialog == SHOWDIALOG_ID_PREFERENCES )
+ aDialog = "PREFERENCES";
+ const ApplicationEvent* pAppEvent = new ApplicationEvent(
+ ApplicationEvent::TYPE_SHOWDIALOG, aDialog);
+ AquaSalInstance::aAppEventList.push_back( pAppEvent );
+ }
+}
+
+-(void)showPreferences: (id) sender
+{
+ (void)sender;
+ YIELD_GUARD;
+
+ [self showDialog: SHOWDIALOG_ID_PREFERENCES];
+}
+-(void)showAbout: (id) sender
+{
+ (void)sender;
+ YIELD_GUARD;
+
+ [self showDialog: SHOWDIALOG_ID_ABOUT];
+}
+@end
+
+
+// FIXME: currently this is leaked
+static MainMenuSelector* pMainMenuSelector = nil;
+
+static void initAppMenu()
+{
+ static bool bOnce = true;
+ if( bOnce )
+ {
+ bOnce = false;
+
+ ResMgr* pMgr = ImplGetResMgr();
+ if( pMgr )
+ {
+ // get the main menu
+ NSMenu* pMainMenu = [NSApp mainMenu];
+ if( pMainMenu != nil )
+ {
+ // create the action selector
+ pMainMenuSelector = [[MainMenuSelector alloc] init];
+
+ // get the proper submenu
+ NSMenu* pAppMenu = [[pMainMenu itemAtIndex: 0] submenu];
+ if( pAppMenu )
+ {
+ // insert about entry
+ OUString aAbout( ResId( SV_STDTEXT_ABOUT, *pMgr ) );
+ NSString* pString = CreateNSString( aAbout );
+ NSMenuItem* pNewItem = [pAppMenu insertItemWithTitle: pString
+ action: @selector(showAbout:)
+ keyEquivalent: @""
+ atIndex: 0];
+ if (pString)
+ [pString release];
+ if( pNewItem )
+ {
+ [pNewItem setTarget: pMainMenuSelector];
+ [pAppMenu insertItem: [NSMenuItem separatorItem] atIndex: 1];
+ }
+
+ // insert preferences entry
+ OUString aPref( ResId( SV_STDTEXT_PREFERENCES, *pMgr ) );
+ pString = CreateNSString( aPref );
+ pNewItem = [pAppMenu insertItemWithTitle: pString
+ action: @selector(showPreferences:)
+ keyEquivalent: @","
+ atIndex: 2];
+ if (pString)
+ [pString release];
+ if( pNewItem )
+ {
+ [pNewItem setKeyEquivalentModifierMask: NSCommandKeyMask];
+ [pNewItem setTarget: pMainMenuSelector];
+ [pAppMenu insertItem: [NSMenuItem separatorItem] atIndex: 3];
+ }
+
+ // WARNING: ultra ugly code ahead
+
+ // rename standard entries
+ // rename "Services"
+ pNewItem = [pAppMenu itemAtIndex: 4];
+ if( pNewItem )
+ {
+ pString = CreateNSString( OUString( ResId( SV_MENU_MAC_SERVICES, *pMgr ) ) );
+ [pNewItem setTitle: pString];
+ if( pString )
+ [pString release];
+ }
+
+ // rename "Hide NewApplication"
+ pNewItem = [pAppMenu itemAtIndex: 6];
+ if( pNewItem )
+ {
+ pString = CreateNSString( OUString( ResId( SV_MENU_MAC_HIDEAPP, *pMgr ) ) );
+ [pNewItem setTitle: pString];
+ if( pString )
+ [pString release];
+ }
+
+ // rename "Hide Others"
+ pNewItem = [pAppMenu itemAtIndex: 7];
+ if( pNewItem )
+ {
+ pString = CreateNSString( OUString( ResId( SV_MENU_MAC_HIDEALL, *pMgr ) ) );
+ [pNewItem setTitle: pString];
+ if( pString )
+ [pString release];
+ }
+
+ // rename "Show all"
+ pNewItem = [pAppMenu itemAtIndex: 8];
+ if( pNewItem )
+ {
+ pString = CreateNSString( OUString( ResId( SV_MENU_MAC_SHOWALL, *pMgr ) ) );
+ [pNewItem setTitle: pString];
+ if( pString )
+ [pString release];
+ }
+
+ // rename "Quit NewApplication"
+ pNewItem = [pAppMenu itemAtIndex: 10];
+ if( pNewItem )
+ {
+ pString = CreateNSString( OUString( ResId( SV_MENU_MAC_QUITAPP, *pMgr ) ) );
+ [pNewItem setTitle: pString];
+ if( pString )
+ [pString release];
+ }
+ }
+ }
+ }
+ }
+}
+
+// =======================================================================
+
+SalMenu* AquaSalInstance::CreateMenu( sal_Bool bMenuBar, Menu* pVCLMenu )
+{
+ initAppMenu();
+
+ AquaSalMenu *pAquaSalMenu = new AquaSalMenu( bMenuBar );
+ pAquaSalMenu->mpVCLMenu = pVCLMenu;
+
+ return pAquaSalMenu;
+}
+
+void AquaSalInstance::DestroyMenu( SalMenu* pSalMenu )
+{
+ delete pSalMenu;
+}
+
+SalMenuItem* AquaSalInstance::CreateMenuItem( const SalItemParams* pItemData )
+{
+ if( !pItemData )
+ return NULL;
+
+ AquaSalMenuItem *pSalMenuItem = new AquaSalMenuItem( pItemData );
+
+ return pSalMenuItem;
+}
+
+void AquaSalInstance::DestroyMenuItem( SalMenuItem* pSalMenuItem )
+{
+ delete pSalMenuItem;
+}
+
+
+// =======================================================================
+
+
+/*
+ * AquaSalMenu
+ */
+
+AquaSalMenu::AquaSalMenu( bool bMenuBar ) :
+ mbMenuBar( bMenuBar ),
+ mpMenu( nil ),
+ mpVCLMenu( NULL ),
+ mpFrame( NULL ),
+ mpParentSalMenu( NULL )
+{
+ if( ! mbMenuBar )
+ {
+ mpMenu = [[SalNSMenu alloc] initWithMenu: this];
+ // With the 10.6 SDK and gcc 4.2.1, we get: class 'NSMenu'
+ // does not implement the 'NSMenuDelegate' protocol. Anyway,
+ // having the menu object be its own delegate object is
+ // apparently what the code does on purpose.
+
+ // So to silcense the warning, instead of:
+ // [mpMenu setDelegate: mpMenu];
+ // do this:
+ objc_msgSend(mpMenu, @selector(setDelegate:), mpMenu);
+ }
+ else
+ {
+ mpMenu = [NSApp mainMenu];
+ }
+ [mpMenu setAutoenablesItems: NO];
+}
+
+AquaSalMenu::~AquaSalMenu()
+{
+ // actually someone should have done AquaSalFrame::SetMenu( NULL )
+ // on our frame, alas it is not so
+ if( mpFrame && AquaSalFrame::isAlive( mpFrame ) && mpFrame->mpMenu == this )
+ const_cast<AquaSalFrame*>(mpFrame)->mpMenu = NULL;
+
+ // this should normally be empty already, but be careful...
+ for( size_t i = 0; i < maButtons.size(); i++ )
+ releaseButtonEntry( maButtons[i] );
+ maButtons.clear();
+
+ // is this leaking in some cases ? the release often leads to a duplicate release
+ // it seems the parent item gets ownership of the menu
+ if( mpMenu )
+ {
+ if( mbMenuBar )
+ {
+ if( pCurrentMenuBar == this )
+ {
+ // if the current menubar gets destroyed, set the default menubar
+ setDefaultMenu();
+ }
+ }
+ else
+ // the system may still hold a reference on mpMenu
+ {
+ // so set the pointer to this AquaSalMenu to NULL
+ // to protect from calling a dead object
+
+ // in ! mbMenuBar case our mpMenu is actually a SalNSMenu*
+ // so we can safely cast here
+ [static_cast<SalNSMenu*>(mpMenu) setSalMenu: NULL];
+ /* #i89860# FIXME:
+ using [autorelease] here (and in AquaSalMenuItem::~AquaSalMenuItem)
+ instead of [release] fixes an occasional crash. That should
+ indicate that we release menus / menu items in the wrong order
+ somewhere, but I could not find that case.
+ */
+ [mpMenu autorelease];
+ }
+ }
+}
+
+sal_Int32 removeUnusedItemsRunner(NSMenu * pMenu)
+{
+ NSArray * elements = [pMenu itemArray];
+ NSEnumerator * it = [elements objectEnumerator];
+ id elem;
+ NSMenuItem * lastDisplayedMenuItem = nil;
+ sal_Int32 drawnItems = 0;
+ bool firstEnabledItemIsNoSeparator = false;
+ while((elem=[it nextObject]) != nil) {
+ NSMenuItem * item = static_cast<NSMenuItem *>(elem);
+ if( (![item isEnabled] && ![item isSeparatorItem]) || ([item isSeparatorItem] && (lastDisplayedMenuItem != nil && [lastDisplayedMenuItem isSeparatorItem])) ) {
+ [[item menu]removeItem:item];
+ } else {
+ if( ! firstEnabledItemIsNoSeparator && [item isSeparatorItem] ) {
+ [[item menu]removeItem:item];
+ } else {
+ firstEnabledItemIsNoSeparator = true;
+ lastDisplayedMenuItem = item;
+ drawnItems++;
+ if( [item hasSubmenu] ) {
+ removeUnusedItemsRunner( [item submenu] );
+ }
+ }
+ }
+ }
+ if( lastDisplayedMenuItem != nil && [lastDisplayedMenuItem isSeparatorItem]) {
+ [[lastDisplayedMenuItem menu]removeItem:lastDisplayedMenuItem];
+ }
+ return drawnItems;
+}
+
+bool AquaSalMenu::ShowNativePopupMenu(FloatingWindow * pWin, const Rectangle& rRect, sal_uLong nFlags)
+{
+ // do not use native popup menu when AQUA_NATIVE_MENUS is set to sal_False
+ if( ! VisibleMenuBar() ) {
+ return false;
+ }
+
+ // set offsets for positioning
+ const float offset = 9.0;
+
+ // get the pointers
+ AquaSalFrame * pParentAquaSalFrame = (AquaSalFrame *) pWin->ImplGetWindowImpl()->mpRealParent->ImplGetFrame();
+ NSWindow* pParentNSWindow = pParentAquaSalFrame->mpNSWindow;
+ NSView* pParentNSView = [pParentNSWindow contentView];
+ NSView* pPopupNSView = ((AquaSalFrame *) pWin->ImplGetWindow()->ImplGetFrame())->mpNSView;
+ NSRect popupFrame = [pPopupNSView frame];
+
+ // since we manipulate the menu below (removing entries)
+ // let's rather make a copy here and work with that
+ NSMenu* pCopyMenu = [mpMenu copy];
+
+ // filter disabled elements
+ removeUnusedItemsRunner( pCopyMenu );
+
+ // create frame rect
+ NSRect displayPopupFrame = NSMakeRect( rRect.Left()+(offset-1), rRect.Top()+(offset+1), popupFrame.size.width, 0 );
+ pParentAquaSalFrame->VCLToCocoa(displayPopupFrame, false);
+
+ // do the same strange semantics as vcl popup windows to arrive at a frame geometry
+ // in mirrored UI case; best done by actually executing the same code
+ sal_uInt16 nArrangeIndex;
+ pWin->SetPosPixel( pWin->ImplCalcPos( pWin, rRect, nFlags, nArrangeIndex ) );
+ displayPopupFrame.origin.x = pWin->ImplGetFrame()->maGeometry.nX - pParentAquaSalFrame->maGeometry.nX + offset;
+ displayPopupFrame.origin.y = pWin->ImplGetFrame()->maGeometry.nY - pParentAquaSalFrame->maGeometry.nY + offset;
+ pParentAquaSalFrame->VCLToCocoa(displayPopupFrame, false);
+
+ // #i111992# if this menu was opened due to a key event, prevent dispatching that yet again
+ if( [pParentNSView respondsToSelector: @selector(clearLastEvent)] )
+ [pParentNSView performSelector:@selector(clearLastEvent)];
+
+ // open popup menu
+ NSPopUpButtonCell * pPopUpButtonCell = [[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO];
+ [pPopUpButtonCell setMenu: pCopyMenu];
+ [pPopUpButtonCell selectItem:nil];
+ [AquaA11yWrapper setPopupMenuOpen: YES];
+ [pPopUpButtonCell performClickWithFrame:displayPopupFrame inView:pParentNSView];
+ [pPopUpButtonCell release];
+ [AquaA11yWrapper setPopupMenuOpen: NO];
+
+ // clean up the copy
+ [pCopyMenu release];
+ return true;
+}
+
+int AquaSalMenu::getItemIndexByPos( sal_uInt16 nPos ) const
+{
+ int nIndex = 0;
+ if( nPos == MENU_APPEND )
+ nIndex = [mpMenu numberOfItems];
+ else
+ nIndex = sal::static_int_cast<int>( mbMenuBar ? nPos+1 : nPos );
+ return nIndex;
+}
+
+const AquaSalFrame* AquaSalMenu::getFrame() const
+{
+ const AquaSalMenu* pMenu = this;
+ while( pMenu && ! pMenu->mpFrame )
+ pMenu = pMenu->mpParentSalMenu;
+ return pMenu ? pMenu->mpFrame : NULL;
+}
+
+void AquaSalMenu::unsetMainMenu()
+{
+ pCurrentMenuBar = NULL;
+
+ // remove items from main menu
+ NSMenu* pMenu = [NSApp mainMenu];
+ for( int nItems = [pMenu numberOfItems]; nItems > 1; nItems-- )
+ [pMenu removeItemAtIndex: 1];
+}
+
+void AquaSalMenu::setMainMenu()
+{
+ DBG_ASSERT( mbMenuBar, "setMainMenu on non menubar" );
+ if( mbMenuBar )
+ {
+ if( pCurrentMenuBar != this )
+ {
+ unsetMainMenu();
+ // insert our items
+ for( unsigned int i = 0; i < maItems.size(); i++ )
+ {
+ NSMenuItem* pItem = maItems[i]->mpMenuItem;
+ [mpMenu insertItem: pItem atIndex: i+1];
+ }
+ pCurrentMenuBar = this;
+
+ // change status item
+ statusLayout();
+ }
+ enableMainMenu( true );
+ }
+}
+
+void AquaSalMenu::setDefaultMenu()
+{
+ NSMenu* pMenu = [NSApp mainMenu];
+
+ unsetMainMenu();
+
+ // insert default items
+ std::vector< NSMenuItem* >& rFallbackMenu( GetSalData()->maFallbackMenu );
+ for( unsigned int i = 0, nAddItems = rFallbackMenu.size(); i < nAddItems; i++ )
+ {
+ NSMenuItem* pItem = rFallbackMenu[i];
+ if( [pItem menu] == nil )
+ [pMenu insertItem: pItem atIndex: i+1];
+ }
+}
+
+void AquaSalMenu::enableMainMenu( bool bEnable )
+{
+ NSMenu* pMainMenu = [NSApp mainMenu];
+ if( pMainMenu )
+ {
+ // enable/disable items from main menu
+ int nItems = [pMainMenu numberOfItems];
+ for( int n = 1; n < nItems; n++ )
+ {
+ NSMenuItem* pItem = [pMainMenu itemAtIndex: n];
+ [pItem setEnabled: bEnable ? YES : NO];
+ }
+ }
+}
+
+void AquaSalMenu::addFallbackMenuItem( NSMenuItem* pNewItem )
+{
+ initAppMenu();
+
+ std::vector< NSMenuItem* >& rFallbackMenu( GetSalData()->maFallbackMenu );
+
+ // prevent duplicate insertion
+ int nItems = rFallbackMenu.size();
+ for( int i = 0; i < nItems; i++ )
+ {
+ if( rFallbackMenu[i] == pNewItem )
+ return;
+ }
+
+ // push the item to the back and retain it
+ [pNewItem retain];
+ rFallbackMenu.push_back( pNewItem );
+
+ if( pCurrentMenuBar == NULL )
+ setDefaultMenu();
+}
+
+void AquaSalMenu::removeFallbackMenuItem( NSMenuItem* pOldItem )
+{
+ std::vector< NSMenuItem* >& rFallbackMenu( GetSalData()->maFallbackMenu );
+
+ // find item
+ unsigned int nItems = rFallbackMenu.size();
+ for( unsigned int i = 0; i < nItems; i++ )
+ {
+ if( rFallbackMenu[i] == pOldItem )
+ {
+ // remove item and release
+ rFallbackMenu.erase( rFallbackMenu.begin() + i );
+ [pOldItem release];
+
+ if( pCurrentMenuBar == NULL )
+ setDefaultMenu();
+
+ return;
+ }
+ }
+}
+
+sal_Bool AquaSalMenu::VisibleMenuBar()
+{
+ // Enable/disable experimental native menus code?
+ //
+ // To disable native menus, set the environment variable AQUA_NATIVE_MENUS to FALSE
+
+ static const char *pExperimental = getenv ("AQUA_NATIVE_MENUS");
+
+ if ( pExperimental && !strcasecmp(pExperimental, "FALSE") )
+ return sal_False;
+
+ // End of experimental code enable/disable part
+
+ return sal_True;
+}
+
+void AquaSalMenu::SetFrame( const SalFrame *pFrame )
+{
+ mpFrame = static_cast<const AquaSalFrame*>(pFrame);
+}
+
+void AquaSalMenu::InsertItem( SalMenuItem* pSalMenuItem, unsigned nPos )
+{
+ AquaSalMenuItem *pAquaSalMenuItem = static_cast<AquaSalMenuItem*>(pSalMenuItem);
+
+ pAquaSalMenuItem->mpParentMenu = this;
+ DBG_ASSERT( pAquaSalMenuItem->mpVCLMenu == NULL ||
+ pAquaSalMenuItem->mpVCLMenu == mpVCLMenu ||
+ mpVCLMenu == NULL,
+ "resetting menu ?" );
+ if( pAquaSalMenuItem->mpVCLMenu )
+ mpVCLMenu = pAquaSalMenuItem->mpVCLMenu;
+
+ if( nPos == MENU_APPEND || nPos == maItems.size() )
+ maItems.push_back( pAquaSalMenuItem );
+ else if( nPos < maItems.size() )
+ maItems.insert( maItems.begin() + nPos, pAquaSalMenuItem );
+ else
+ {
+ OSL_FAIL( "invalid item index in insert" );
+ return;
+ }
+
+ if( ! mbMenuBar || pCurrentMenuBar == this )
+ [mpMenu insertItem: pAquaSalMenuItem->mpMenuItem atIndex: getItemIndexByPos(nPos)];
+}
+
+void AquaSalMenu::RemoveItem( unsigned nPos )
+{
+ AquaSalMenuItem* pRemoveItem = NULL;
+ if( nPos == MENU_APPEND || nPos == (maItems.size()-1) )
+ {
+ pRemoveItem = maItems.back();
+ maItems.pop_back();
+ }
+ else if( nPos < maItems.size() )
+ {
+ pRemoveItem = maItems[ nPos ];
+ maItems.erase( maItems.begin()+nPos );
+ }
+ else
+ {
+ OSL_FAIL( "invalid item index in remove" );
+ return;
+ }
+
+ pRemoveItem->mpParentMenu = NULL;
+
+ if( ! mbMenuBar || pCurrentMenuBar == this )
+ [mpMenu removeItemAtIndex: getItemIndexByPos(nPos)];
+}
+
+void AquaSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsigned /*nPos*/ )
+{
+ AquaSalMenuItem *pAquaSalMenuItem = static_cast<AquaSalMenuItem*>(pSalMenuItem);
+ AquaSalMenu *subAquaSalMenu = static_cast<AquaSalMenu*>(pSubMenu);
+
+ if (subAquaSalMenu)
+ {
+ pAquaSalMenuItem->mpSubMenu = subAquaSalMenu;
+ if( subAquaSalMenu->mpParentSalMenu == NULL )
+ {
+ subAquaSalMenu->mpParentSalMenu = this;
+ [pAquaSalMenuItem->mpMenuItem setSubmenu: subAquaSalMenu->mpMenu];
+
+ // set title of submenu
+ [subAquaSalMenu->mpMenu setTitle: [pAquaSalMenuItem->mpMenuItem title]];
+ }
+ else if( subAquaSalMenu->mpParentSalMenu != this )
+ {
+ // cocoa doesn't allow menus to be submenus of multiple
+ // menu items, so place a copy in the menu item instead ?
+ // let's hope that NSMenu copy does the right thing
+ NSMenu* pCopy = [subAquaSalMenu->mpMenu copy];
+ [pAquaSalMenuItem->mpMenuItem setSubmenu: pCopy];
+
+ // set title of submenu
+ [pCopy setTitle: [pAquaSalMenuItem->mpMenuItem title]];
+ }
+ }
+ else
+ {
+ if( pAquaSalMenuItem->mpSubMenu )
+ {
+ if( pAquaSalMenuItem->mpSubMenu->mpParentSalMenu == this )
+ pAquaSalMenuItem->mpSubMenu->mpParentSalMenu = NULL;
+ }
+ pAquaSalMenuItem->mpSubMenu = NULL;
+ [pAquaSalMenuItem->mpMenuItem setSubmenu: nil];
+ }
+}
+
+void AquaSalMenu::CheckItem( unsigned nPos, sal_Bool bCheck )
+{
+ if( nPos < maItems.size() )
+ {
+ NSMenuItem* pItem = maItems[nPos]->mpMenuItem;
+ [pItem setState: bCheck ? NSOnState : NSOffState];
+ }
+}
+
+void AquaSalMenu::EnableItem( unsigned nPos, sal_Bool bEnable )
+{
+ if( nPos < maItems.size() )
+ {
+ NSMenuItem* pItem = maItems[nPos]->mpMenuItem;
+ [pItem setEnabled: bEnable ? YES : NO];
+ }
+}
+
+void AquaSalMenu::SetItemImage( unsigned /*nPos*/, SalMenuItem* pSMI, const Image& rImage )
+{
+ AquaSalMenuItem* pSalMenuItem = static_cast<AquaSalMenuItem*>( pSMI );
+ if( ! pSalMenuItem || ! pSalMenuItem->mpMenuItem )
+ return;
+
+ NSImage* pImage = CreateNSImage( rImage );
+
+ [pSalMenuItem->mpMenuItem setImage: pImage];
+ if( pImage )
+ [pImage release];
+}
+
+void AquaSalMenu::SetItemText( unsigned /*i_nPos*/, SalMenuItem* i_pSalMenuItem, const OUString& i_rText )
+{
+ if (!i_pSalMenuItem)
+ return;
+
+ AquaSalMenuItem *pAquaSalMenuItem = (AquaSalMenuItem *) i_pSalMenuItem;
+
+ // Delete mnemonics
+ OUString aText( comphelper::string::remove(i_rText, '~') );
+
+ /* #i90015# until there is a correct solution
+ strip out any appended (.*) in menubar entries
+ */
+ if( mbMenuBar )
+ {
+ sal_Int32 nPos = aText.lastIndexOf( '(' );
+ if( nPos != -1 )
+ {
+ sal_Int32 nPos2 = aText.indexOf( ')' );
+ if( nPos2 != -1 )
+ aText = aText.replaceAt( nPos, nPos2-nPos+1, "" );
+ }
+ }
+
+ NSString* pString = CreateNSString( aText );
+ if (pString)
+ {
+ [pAquaSalMenuItem->mpMenuItem setTitle: pString];
+ // if the menu item has a submenu, change its title as well
+ if (pAquaSalMenuItem->mpSubMenu)
+ [pAquaSalMenuItem->mpSubMenu->mpMenu setTitle: pString];
+ [pString release];
+ }
+}
+
+void AquaSalMenu::SetAccelerator( unsigned /*nPos*/, SalMenuItem* pSalMenuItem, const KeyCode& rKeyCode, const OUString& /*rKeyName*/ )
+{
+ sal_uInt16 nModifier;
+ sal_Unicode nCommandKey = 0;
+
+ sal_uInt16 nKeyCode=rKeyCode.GetCode();
+ if( nKeyCode )
+ {
+ if ((nKeyCode>=KEY_A) && (nKeyCode<=KEY_Z)) // letter A..Z
+ nCommandKey = nKeyCode-KEY_A + 'a';
+ else if ((nKeyCode>=KEY_0) && (nKeyCode<=KEY_9)) // numbers 0..9
+ nCommandKey = nKeyCode-KEY_0 + '0';
+ else if ((nKeyCode>=KEY_F1) && (nKeyCode<=KEY_F26)) // function keys F1..F26
+ nCommandKey = nKeyCode-KEY_F1 + NSF1FunctionKey;
+ else if( nKeyCode == KEY_REPEAT )
+ nCommandKey = NSRedoFunctionKey;
+ else if( nKeyCode == KEY_SPACE )
+ nCommandKey = ' ';
+ else
+ {
+ switch (nKeyCode)
+ {
+ case KEY_ADD:
+ nCommandKey='+';
+ break;
+ case KEY_SUBTRACT:
+ nCommandKey='-';
+ break;
+ case KEY_MULTIPLY:
+ nCommandKey='*';
+ break;
+ case KEY_DIVIDE:
+ nCommandKey='/';
+ break;
+ case KEY_POINT:
+ nCommandKey='.';
+ break;
+ case KEY_LESS:
+ nCommandKey='<';
+ break;
+ case KEY_GREATER:
+ nCommandKey='>';
+ break;
+ case KEY_EQUAL:
+ nCommandKey='=';
+ break;
+ }
+ }
+ }
+ else // not even a code ? nonsense -> ignore
+ return;
+
+ DBG_ASSERT( nCommandKey, "unmapped accelerator key" );
+
+ nModifier=rKeyCode.GetAllModifier();
+
+ // should always use the command key
+ int nItemModifier = 0;
+
+ if (nModifier & KEY_SHIFT)
+ {
+ nItemModifier |= NSShiftKeyMask; // actually useful only for function keys
+ if( nKeyCode >= KEY_A && nKeyCode <= KEY_Z )
+ nCommandKey = nKeyCode - KEY_A + 'A';
+ }
+
+ if (nModifier & KEY_MOD1)
+ nItemModifier |= NSCommandKeyMask;
+
+ if(nModifier & KEY_MOD2)
+ nItemModifier |= NSAlternateKeyMask;
+
+ if(nModifier & KEY_MOD3)
+ nItemModifier |= NSControlKeyMask;
+
+ AquaSalMenuItem *pAquaSalMenuItem = (AquaSalMenuItem *) pSalMenuItem;
+ NSString* pString = CreateNSString( OUString( &nCommandKey, 1 ) );
+ [pAquaSalMenuItem->mpMenuItem setKeyEquivalent: pString];
+ [pAquaSalMenuItem->mpMenuItem setKeyEquivalentModifierMask: nItemModifier];
+ if (pString)
+ [pString release];
+}
+
+void AquaSalMenu::GetSystemMenuData( SystemMenuData* )
+{
+}
+
+AquaSalMenu::MenuBarButtonEntry* AquaSalMenu::findButtonItem( sal_uInt16 i_nItemId )
+{
+ for( size_t i = 0; i < maButtons.size(); ++i )
+ {
+ if( maButtons[i].maButton.mnId == i_nItemId )
+ return &maButtons[i];
+ }
+ return NULL;
+}
+
+void AquaSalMenu::statusLayout()
+{
+ if( GetSalData()->mpStatusItem )
+ {
+ NSView* pNSView = [GetSalData()->mpStatusItem view];
+ if( [pNSView isMemberOfClass: [OOStatusItemView class]] ) // well of course it is
+ [(OOStatusItemView*)pNSView layout];
+ else
+ OSL_FAIL( "someone stole our status view" );
+ }
+}
+
+void AquaSalMenu::releaseButtonEntry( MenuBarButtonEntry& i_rEntry )
+{
+ if( i_rEntry.mpNSImage )
+ {
+ [i_rEntry.mpNSImage release];
+ i_rEntry.mpNSImage = nil;
+ }
+ if( i_rEntry.mpToolTipString )
+ {
+ [i_rEntry.mpToolTipString release];
+ i_rEntry.mpToolTipString = nil;
+ }
+}
+
+bool AquaSalMenu::AddMenuBarButton( const SalMenuButtonItem& i_rNewItem )
+{
+ if( ! mbMenuBar || ! VisibleMenuBar() )
+ return false;
+
+ MenuBarButtonEntry* pEntry = findButtonItem( i_rNewItem.mnId );
+ if( pEntry )
+ {
+ releaseButtonEntry( *pEntry );
+ pEntry->maButton = i_rNewItem;
+ pEntry->mpNSImage = CreateNSImage( i_rNewItem.maImage );
+ if( i_rNewItem.maToolTipText.getLength() )
+ pEntry->mpToolTipString = CreateNSString( i_rNewItem.maToolTipText );
+ }
+ else
+ {
+ maButtons.push_back( MenuBarButtonEntry( i_rNewItem ) );
+ maButtons.back().mpNSImage = CreateNSImage( i_rNewItem.maImage );
+ maButtons.back().mpToolTipString = CreateNSString( i_rNewItem.maToolTipText );
+ }
+
+ // lazy create status item
+ SalData::getStatusItem();
+
+ if( pCurrentMenuBar == this )
+ statusLayout();
+
+ return true;
+}
+
+void AquaSalMenu::RemoveMenuBarButton( sal_uInt16 i_nId )
+{
+ MenuBarButtonEntry* pEntry = findButtonItem( i_nId );
+ if( pEntry )
+ {
+ releaseButtonEntry( *pEntry );
+ // note: vector guarantees that its contents are in a plain array
+ maButtons.erase( maButtons.begin() + (pEntry - &maButtons[0]) );
+ }
+
+ if( pCurrentMenuBar == this )
+ statusLayout();
+}
+
+Rectangle AquaSalMenu::GetMenuBarButtonRectPixel( sal_uInt16 i_nItemId, SalFrame* i_pReferenceFrame )
+{
+ if( ! i_pReferenceFrame || ! AquaSalFrame::isAlive( static_cast<AquaSalFrame*>(i_pReferenceFrame) ) )
+ return Rectangle();
+
+ MenuBarButtonEntry* pEntry = findButtonItem( i_nItemId );
+
+ if( ! pEntry )
+ return Rectangle();
+
+ NSStatusItem* pItem = SalData::getStatusItem();
+ if( ! pItem )
+ return Rectangle();
+
+ NSView* pNSView = [pItem view];
+ if( ! pNSView )
+ return Rectangle();
+ NSWindow* pNSWin = [pNSView window];
+ if( ! pNSWin )
+ return Rectangle();
+
+ NSRect aRect = [pNSWin frame];
+ aRect.origin = [pNSWin convertBaseToScreen: NSMakePoint( 0, 0 )];
+
+ // make coordinates relative to reference frame
+ static_cast<AquaSalFrame*>(i_pReferenceFrame)->CocoaToVCL( aRect.origin );
+ aRect.origin.x -= i_pReferenceFrame->maGeometry.nX;
+ aRect.origin.y -= i_pReferenceFrame->maGeometry.nY + aRect.size.height;
+
+ return Rectangle( Point(static_cast<long int>(aRect.origin.x),
+ static_cast<long int>(aRect.origin.y)
+ ),
+ Size( static_cast<long int>(aRect.size.width),
+ static_cast<long int>(aRect.size.height)
+ )
+ );
+}
+
+// =======================================================================
+
+/*
+ * SalMenuItem
+ */
+
+AquaSalMenuItem::AquaSalMenuItem( const SalItemParams* pItemData ) :
+ mnId( pItemData->nId ),
+ mpVCLMenu( pItemData->pMenu ),
+ mpParentMenu( NULL ),
+ mpSubMenu( NULL ),
+ mpMenuItem( nil )
+{
+ // Delete mnemonics
+ OUString aText( comphelper::string::remove(pItemData->aText, '~') );
+
+ if (pItemData->eType == MENUITEM_SEPARATOR)
+ {
+ mpMenuItem = [NSMenuItem separatorItem];
+ // these can go occasionally go in and out of a menu, ensure their lifecycle
+ // also for the release in AquaSalMenuItem destructor
+ [mpMenuItem retain];
+ }
+ else
+ {
+ mpMenuItem = [[SalNSMenuItem alloc] initWithMenuItem: this];
+ [mpMenuItem setEnabled: YES];
+ NSString* pString = CreateNSString( aText );
+ if (pString)
+ {
+ [mpMenuItem setTitle: pString];
+ [pString release];
+ }
+ // anything but a separator should set a menu to dispatch to
+ DBG_ASSERT( mpVCLMenu, "no menu" );
+ }
+}
+
+AquaSalMenuItem::~AquaSalMenuItem()
+{
+ /* #i89860# FIXME:
+ using [autorelease] here (and in AquaSalMenu:::~AquaSalMenu) instead of
+ [release] fixes an occasional crash. That should indicate that we release
+ menus / menu items in the wrong order somewhere, but I
+ could not find that case.
+ */
+ if( mpMenuItem )
+ [mpMenuItem autorelease];
+}
+
+// -------------------------------------------------------------------
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/salnativewidgets.cxx b/vcl/osx/salnativewidgets.cxx
new file mode 100644
index 000000000000..ec6ca1d9c72f
--- /dev/null
+++ b/vcl/osx/salnativewidgets.cxx
@@ -0,0 +1,1462 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "vcl/salnativewidgets.hxx"
+#include "vcl/decoview.hxx"
+#include "vcl/svapp.hxx"
+#include "vcl/timer.hxx"
+
+#include "quartz/salgdi.h"
+#include "osx/salnativewidgets.h"
+#include "osx/saldata.hxx"
+#include "osx/salframe.h"
+
+#include "premac.h"
+#include <Carbon/Carbon.h>
+#include "postmac.h"
+
+#ifndef NSAppKitVersionNumber10_7
+#define NSAppKitVersionNumber10_7 1138
+#endif
+
+class AquaBlinker : public Timer
+{
+ AquaSalFrame* mpFrame;
+ Rectangle maInvalidateRect;
+
+ AquaBlinker( AquaSalFrame* pFrame, const Rectangle& rRect )
+ : mpFrame( pFrame ), maInvalidateRect( rRect )
+ {
+ mpFrame->maBlinkers.push_back( this );
+ }
+
+ public:
+
+ static void Blink( AquaSalFrame*, const Rectangle&, int nTimeout = 80 );
+
+ virtual void Timeout()
+ {
+ Stop();
+ if( AquaSalFrame::isAlive( mpFrame ) && mpFrame->mbShown )
+ {
+ mpFrame->maBlinkers.remove( this );
+ mpFrame->SendPaintEvent( &maInvalidateRect );
+ }
+ delete this;
+ }
+};
+
+void AquaBlinker::Blink( AquaSalFrame* pFrame, const Rectangle& rRect, int nTimeout )
+{
+ // prevent repeated paints from triggering themselves all the time
+ for( std::list< AquaBlinker* >::const_iterator it = pFrame->maBlinkers.begin();
+ it != pFrame->maBlinkers.end(); ++it )
+ {
+ if( (*it)->maInvalidateRect == rRect )
+ return;
+ }
+ AquaBlinker* pNew = new AquaBlinker( pFrame, rRect );
+ pNew->SetTimeout( nTimeout );
+ pNew->Start();
+}
+
+// Helper returns an HIRect
+
+static HIRect ImplGetHIRectFromRectangle(Rectangle aRect)
+{
+ HIRect aHIRect;
+ aHIRect.origin.x = static_cast<float>(aRect.Left());
+ aHIRect.origin.y = static_cast<float>(aRect.Top());
+ aHIRect.size.width = static_cast<float>(aRect.GetWidth());
+ aHIRect.size.height = static_cast<float>(aRect.GetHeight());
+ return aHIRect;
+}
+
+static ThemeButtonValue ImplGetButtonValue( ButtonValue aButtonValue )
+{
+ switch( aButtonValue )
+ {
+ case BUTTONVALUE_ON:
+ return kThemeButtonOn;
+ break;
+
+ case BUTTONVALUE_OFF:
+ return kThemeButtonOff;
+ break;
+
+ case BUTTONVALUE_MIXED:
+ case BUTTONVALUE_DONTKNOW:
+ default:
+ return kThemeButtonMixed;
+ break;
+ }
+}
+
+static bool AquaGetScrollRect( /* TODO: int nScreen, */ ControlPart nPart,
+ const Rectangle& rControlRect, Rectangle& rResultRect )
+{
+ bool bRetVal = true;
+ rResultRect = rControlRect;
+
+ switch( nPart )
+ {
+ case PART_BUTTON_UP:
+ if (NSAppKitVersionNumber < NSAppKitVersionNumber10_7)
+ {
+ if( GetSalData()->mbIsScrollbarDoubleMax )
+ rResultRect.Top() = rControlRect.Bottom() - 2*BUTTON_HEIGHT;
+ rResultRect.Bottom() = rResultRect.Top() + BUTTON_HEIGHT;
+ }
+ else
+ {
+ rResultRect.Bottom() = rResultRect.Top();
+ }
+ break;
+
+ case PART_BUTTON_DOWN:
+ if (NSAppKitVersionNumber < NSAppKitVersionNumber10_7)
+ {
+ rResultRect.Top() = rControlRect.Bottom() - BUTTON_HEIGHT;
+ }
+ else
+ {
+ rResultRect.Top() = rResultRect.Bottom();
+ }
+ break;
+
+ case PART_BUTTON_LEFT:
+ if (NSAppKitVersionNumber < NSAppKitVersionNumber10_7)
+ {
+ if( GetSalData()->mbIsScrollbarDoubleMax )
+ rResultRect.Left() = rControlRect.Right() - 2*BUTTON_WIDTH;
+ rResultRect.Right() = rResultRect.Left() + BUTTON_WIDTH;
+ }
+ else
+ {
+ rResultRect.Right() = rResultRect.Left();
+ }
+ break;
+
+ case PART_BUTTON_RIGHT:
+ if (NSAppKitVersionNumber < NSAppKitVersionNumber10_7)
+ {
+ rResultRect.Left() = rControlRect.Right() - BUTTON_WIDTH;
+ }
+ else
+ {
+ rResultRect.Left() = rResultRect.Right();
+ }
+ break;
+
+ case PART_TRACK_HORZ_AREA:
+ if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
+ break;
+ rResultRect.Right() -= BUTTON_WIDTH + 1;
+ if( GetSalData()->mbIsScrollbarDoubleMax )
+ rResultRect.Right() -= BUTTON_WIDTH;
+ else
+ rResultRect.Left() += BUTTON_WIDTH + 1;
+ break;
+
+ case PART_TRACK_VERT_AREA:
+ if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
+ break;
+ rResultRect.Bottom() -= BUTTON_HEIGHT + 1;
+ if( GetSalData()->mbIsScrollbarDoubleMax )
+ rResultRect.Bottom() -= BUTTON_HEIGHT;
+ else
+ rResultRect.Top() += BUTTON_HEIGHT + 1;
+ break;
+ case PART_THUMB_HORZ:
+ if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
+ break;
+ if( GetSalData()->mbIsScrollbarDoubleMax )
+ {
+ rResultRect.Left() += 8;
+ rResultRect.Right() += 6;
+ }
+ else
+ {
+ rResultRect.Left() += 4;
+ rResultRect.Right() += 4;
+ }
+ break;
+ case PART_THUMB_VERT:
+ if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
+ break;
+ if( GetSalData()->mbIsScrollbarDoubleMax )
+ {
+ rResultRect.Top() += 8;
+ rResultRect.Bottom() += 8;
+ }
+ else
+ {
+ rResultRect.Top() += 4;
+ rResultRect.Bottom() += 4;
+ }
+ break;
+ case PART_TRACK_HORZ_LEFT:
+ if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
+ break;
+ if( GetSalData()->mbIsScrollbarDoubleMax )
+ rResultRect.Right() += 8;
+ else
+ rResultRect.Right() += 4;
+ break;
+ case PART_TRACK_HORZ_RIGHT:
+ if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
+ break;
+ if( GetSalData()->mbIsScrollbarDoubleMax )
+ rResultRect.Left() += 6;
+ else
+ rResultRect.Left() += 4;
+ break;
+ case PART_TRACK_VERT_UPPER:
+ if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
+ break;
+ if( GetSalData()->mbIsScrollbarDoubleMax )
+ rResultRect.Bottom() += 8;
+ else
+ rResultRect.Bottom() += 4;
+ break;
+ case PART_TRACK_VERT_LOWER:
+ if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7)
+ break;
+ if( GetSalData()->mbIsScrollbarDoubleMax )
+ rResultRect.Top() += 8;
+ else
+ rResultRect.Top() += 4;
+ break;
+ default:
+ bRetVal = false;
+ }
+
+ return bRetVal;
+}
+
+/*
+ * IsNativeControlSupported()
+ * --------------------------
+ * Returns sal_True if the platform supports native
+ * drawing of the control defined by nPart.
+ *
+ */
+sal_Bool AquaSalGraphics::IsNativeControlSupported( ControlType nType, ControlPart nPart )
+{
+ bool bOk = sal_False;
+
+ // Native controls are now defaults
+ // If you want to disable experimental native controls code,
+ // just set the environment variable SAL_NO_NWF to something
+ // and vcl controls will be used as default again.
+
+ switch( nType )
+ {
+ case CTRL_PUSHBUTTON:
+ case CTRL_RADIOBUTTON:
+ case CTRL_CHECKBOX:
+ case CTRL_LISTNODE:
+ if( nPart == PART_ENTIRE_CONTROL )
+ return true;
+ break;
+
+ case CTRL_SCROLLBAR:
+ if( nPart == PART_DRAW_BACKGROUND_HORZ ||
+ nPart == PART_DRAW_BACKGROUND_VERT ||
+ nPart == PART_ENTIRE_CONTROL ||
+ nPart == HAS_THREE_BUTTONS )
+ return true;
+ break;
+
+ case CTRL_SLIDER:
+ if( nPart == PART_TRACK_HORZ_AREA || nPart == PART_TRACK_VERT_AREA )
+ return true;
+ break;
+
+ case CTRL_EDITBOX:
+ if( nPart == PART_ENTIRE_CONTROL ||
+ nPart == HAS_BACKGROUND_TEXTURE )
+ return true;
+ break;
+
+ case CTRL_MULTILINE_EDITBOX:
+ if( nPart == PART_ENTIRE_CONTROL ||
+ nPart == HAS_BACKGROUND_TEXTURE )
+ return true;
+ break;
+
+ case CTRL_SPINBOX:
+ if( nPart == PART_ENTIRE_CONTROL ||
+ nPart == PART_ALL_BUTTONS ||
+ nPart == HAS_BACKGROUND_TEXTURE )
+ return true;
+ break;
+
+ case CTRL_SPINBUTTONS:
+ return false;
+ break;
+
+ case CTRL_COMBOBOX:
+ if( nPart == PART_ENTIRE_CONTROL ||
+ nPart == HAS_BACKGROUND_TEXTURE )
+ return true;
+ break;
+
+ case CTRL_LISTBOX:
+ if( nPart == PART_ENTIRE_CONTROL ||
+ nPart == PART_WINDOW ||
+ nPart == HAS_BACKGROUND_TEXTURE ||
+ nPart == PART_SUB_EDIT
+ )
+ return true;
+ break;
+
+ case CTRL_TAB_ITEM:
+ case CTRL_TAB_PANE:
+ case CTRL_TAB_BODY: // see vcl/source/window/tabpage.cxx
+ if( nPart == PART_ENTIRE_CONTROL ||
+ nPart == PART_TABS_DRAW_RTL ||
+ nPart == HAS_BACKGROUND_TEXTURE )
+ return true;
+ break;
+
+ // when PART_BUTTON is used, toolbar icons are not highlighted when mouse rolls over.
+ // More Aqua compliant
+ case CTRL_TOOLBAR:
+ if( nPart == PART_ENTIRE_CONTROL ||
+ nPart == PART_DRAW_BACKGROUND_HORZ ||
+ nPart == PART_DRAW_BACKGROUND_VERT)
+ return true;
+ break;
+
+ case CTRL_WINDOW_BACKGROUND:
+ if ( nPart == PART_BACKGROUND_WINDOW ||
+ nPart == PART_BACKGROUND_DIALOG )
+ return true;
+ break;
+
+ case CTRL_MENUBAR:
+ if( nPart == PART_ENTIRE_CONTROL )
+ return true;
+ break;
+
+ case CTRL_TOOLTIP: // ** TO DO
+ break;
+
+ case CTRL_MENU_POPUP:
+ if( nPart == PART_ENTIRE_CONTROL ||
+ nPart == PART_MENU_ITEM ||
+ nPart == PART_MENU_ITEM_CHECK_MARK ||
+ nPart == PART_MENU_ITEM_RADIO_MARK)
+ return true;
+ break;
+ case CTRL_PROGRESS:
+ case CTRL_INTROPROGRESS:
+ if( nPart == PART_ENTIRE_CONTROL )
+ return true;
+ break;
+ case CTRL_FRAME:
+ if( nPart == PART_BORDER )
+ return true;
+ break;
+ case CTRL_LISTNET:
+ if( nPart == PART_ENTIRE_CONTROL )
+ return true;
+ break;
+ }
+
+ return bOk;
+}
+
+/*
+ * HitTestNativeControl()
+ *
+ * If the return value is sal_True, bIsInside contains information whether
+ * aPos was or was not inside the native widget specified by the
+ * nType/nPart combination.
+ */
+sal_Bool AquaSalGraphics::hitTestNativeControl( ControlType nType, ControlPart nPart, const Rectangle& rControlRegion,
+ const Point& rPos, sal_Bool& rIsInside )
+{
+ if ( nType == CTRL_SCROLLBAR )
+ {
+ Rectangle aRect;
+ bool bValid = AquaGetScrollRect( /* TODO: m_nScreen */ nPart, rControlRegion, aRect );
+ rIsInside = bValid ? aRect.IsInside( rPos ) : sal_False;
+ if( NSAppKitVersionNumber < NSAppKitVersionNumber10_7 &&
+ GetSalData()->mbIsScrollbarDoubleMax )
+ {
+ // in double max mode the actual trough is a little smaller than the track
+ // there is some visual filler that is not sensitive
+ if( bValid && rIsInside )
+ {
+ if( nPart == PART_TRACK_HORZ_AREA )
+ {
+ // the left 4 pixels are not hit sensitive
+ if( rPos.X() - aRect.Left() < 4 )
+ rIsInside = sal_False;
+ }
+ else if( nPart == PART_TRACK_VERT_AREA )
+ {
+ // the top 4 pixels are not hit sensitive
+ if( rPos.Y() - aRect.Top() < 4 )
+ rIsInside = sal_False;
+ }
+ }
+ }
+ return bValid;
+ } // CTRL_SCROLLBAR
+
+ return sal_False;
+}
+
+/*
+ kThemeStateInactive = 0,
+ kThemeStateActive = 1,
+ kThemeStatePressed = 2,
+ kThemeStateRollover = 6,
+ kThemeStateUnavailable = 7,
+ kThemeStateUnavailableInactive = 8
+ kThemeStatePressedUp = 2,
+ kThemeStatePressedDown = 3
+
+#define CTRL_STATE_ENABLED 0x0001
+#define CTRL_STATE_FOCUSED 0x0002
+#define CTRL_STATE_PRESSED 0x0004
+#define CTRL_STATE_ROLLOVER 0x0008
+#define CTRL_STATE_HIDDEN 0x0010
+#define CTRL_STATE_DEFAULT 0x0020
+#define CTRL_STATE_SELECTED 0x0040
+#define CTRL_CACHING_ALLOWED 0x8000 // set when the control is completely visible (i.e. not clipped)
+*/
+UInt32 AquaSalGraphics::getState( ControlState nState )
+{
+ const bool bDrawActive = mpFrame ? ([mpFrame->getNSWindow() isKeyWindow] ? true : false) : true;
+ if( (nState & CTRL_STATE_ENABLED) == 0 || ! bDrawActive )
+ {
+ if( (nState & CTRL_STATE_HIDDEN) == 0 )
+ return kThemeStateInactive;
+ else
+ return kThemeStateUnavailableInactive;
+ }
+
+ if( (nState & CTRL_STATE_HIDDEN) != 0 )
+ return kThemeStateUnavailable;
+
+ if( (nState & CTRL_STATE_PRESSED) != 0 )
+ return kThemeStatePressed;
+
+ return kThemeStateActive;
+}
+
+UInt32 AquaSalGraphics::getTrackState( ControlState nState )
+{
+ const bool bDrawActive = mpFrame ? ([mpFrame->getNSWindow() isKeyWindow] ? true : false) : true;
+ if( (nState & CTRL_STATE_ENABLED) == 0 || ! bDrawActive )
+ return kThemeTrackInactive;
+
+ return kThemeTrackActive;
+}
+
+/*
+ * DrawNativeControl()
+ *
+ * Draws the requested control described by nPart/nState.
+ *
+ * rControlRegion: The bounding region of the complete control in VCL frame coordinates.
+ * aValue: An optional value (tristate/numerical/string)
+ * aCaption: A caption or title string (like button text etc)
+ */
+sal_Bool AquaSalGraphics::drawNativeControl(ControlType nType,
+ ControlPart nPart,
+ const Rectangle& rControlRegion,
+ ControlState nState,
+ const ImplControlValue& aValue,
+ const OUString& )
+{
+ sal_Bool bOK = sal_False;
+
+ if( ! CheckContext() )
+ return false;
+
+ CGContextSaveGState( mrContext );
+
+ Rectangle buttonRect = rControlRegion;
+ HIRect rc = ImplGetHIRectFromRectangle(buttonRect);
+
+ switch( nType )
+ {
+
+ case CTRL_COMBOBOX:
+ if ( nPart == HAS_BACKGROUND_TEXTURE ||
+ nPart == PART_ENTIRE_CONTROL )
+ {
+ HIThemeButtonDrawInfo aComboInfo;
+ aComboInfo.version = 0;
+ aComboInfo.kind = kThemeComboBox;
+ aComboInfo.state = getState( nState );
+ aComboInfo.value = kThemeButtonOn;
+ aComboInfo.adornment = kThemeAdornmentNone;
+
+ if( (nState & CTRL_STATE_FOCUSED) != 0 )
+ aComboInfo.adornment |= kThemeAdornmentFocus;
+
+ HIThemeDrawButton(&rc, &aComboInfo, mrContext, kHIThemeOrientationNormal,&rc);
+ bOK = true;
+ }
+ break;
+
+ case CTRL_TOOLBAR:
+ {
+ HIThemeMenuItemDrawInfo aMenuItemDrawInfo;
+ aMenuItemDrawInfo.version = 0;
+ aMenuItemDrawInfo.state = kThemeMenuActive;
+ aMenuItemDrawInfo.itemType = kThemeMenuItemHierBackground;
+ HIThemeDrawMenuItem(&rc,&rc,&aMenuItemDrawInfo,mrContext,kHIThemeOrientationNormal,NULL);
+ bOK = true;
+ }
+ break;
+
+ case CTRL_WINDOW_BACKGROUND:
+ {
+ HIThemeBackgroundDrawInfo aThemeBackgroundInfo;
+ aThemeBackgroundInfo.version = 0;
+ aThemeBackgroundInfo.state = getState( nState );
+ aThemeBackgroundInfo.kind = kThemeBrushDialogBackgroundActive;
+ // FIXME: without this magical offset there is a 2 pixel black border on the right and bottom
+ rc.size.width += 2;
+ rc.size.height += 2;
+
+ HIThemeApplyBackground( &rc, &aThemeBackgroundInfo, mrContext, kHIThemeOrientationNormal);
+ CGContextFillRect( mrContext, rc );
+ bOK = true;
+ }
+ break;
+
+ case CTRL_MENUBAR:
+ case CTRL_MENU_POPUP:
+ {
+ if ((nPart == PART_ENTIRE_CONTROL) || (nPart == PART_MENU_ITEM )|| (nPart == HAS_BACKGROUND_TEXTURE ))
+ {
+ // FIXME: without this magical offset there is a 2 pixel black border on the right
+ rc.size.width += 2;
+
+ HIThemeMenuDrawInfo aMenuInfo;
+ aMenuInfo.version = 0;
+ aMenuInfo.menuType = kThemeMenuTypePullDown;
+
+ HIThemeMenuItemDrawInfo aMenuItemDrawInfo;
+ // the Aqua grey theme when the item is selected is drawn here.
+ aMenuItemDrawInfo.itemType = kThemeMenuItemPlain;
+
+ if ((nPart == PART_MENU_ITEM ) && (nState & CTRL_STATE_SELECTED))
+ {
+ // the blue theme when the item is selected is drawn here.
+ aMenuItemDrawInfo.state = kThemeMenuSelected;
+ }
+ else
+ {
+ // normal color for non selected item
+ aMenuItemDrawInfo.state = kThemeMenuActive;
+ }
+
+ // repaints the background of the pull down menu
+ HIThemeDrawMenuBackground(&rc,&aMenuInfo,mrContext,kHIThemeOrientationNormal);
+
+ // repaints the item either blue (selected) and/or Aqua grey (active only)
+ HIThemeDrawMenuItem(&rc,&rc,&aMenuItemDrawInfo,mrContext,kHIThemeOrientationNormal,&rc);
+
+ bOK = true;
+ }
+ else if(( nPart == PART_MENU_ITEM_CHECK_MARK )||( nPart == PART_MENU_ITEM_RADIO_MARK )) {
+ if( nState & CTRL_STATE_PRESSED ) {//checked, else it is not displayed (see vcl/source/window/menu.cxx)
+ HIThemeTextInfo aTextInfo;
+ aTextInfo.version = 0;
+ aTextInfo.state = ((nState & CTRL_STATE_ENABLED)==0) ? kThemeStateInactive: kThemeStateActive;
+ aTextInfo.fontID = kThemeMenuItemMarkFont;
+ aTextInfo.horizontalFlushness=kHIThemeTextHorizontalFlushCenter;
+ aTextInfo.verticalFlushness=kHIThemeTextVerticalFlushTop;
+ aTextInfo.options=kHIThemeTextBoxOptionNone;
+ aTextInfo.truncationPosition=kHIThemeTextTruncationNone;
+ //aTextInfo.truncationMaxLines unused because of kHIThemeTextTruncationNone
+
+ if( nState & CTRL_STATE_SELECTED) aTextInfo.state = kThemeStatePressed; //item highlighted
+
+ UniChar mark=( nPart == PART_MENU_ITEM_CHECK_MARK ) ? kCheckUnicode: kBulletUnicode;//0x2713;
+ CFStringRef cfString = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, &mark, 1, kCFAllocatorNull);
+ HIThemeDrawTextBox(cfString, &rc, &aTextInfo, mrContext, kHIThemeOrientationNormal);
+ if (cfString)
+ CFRelease(cfString);
+
+ bOK = true;
+ }
+ }
+ }
+ break;
+
+ case CTRL_PUSHBUTTON:
+ {
+ // [ FIXME] : instead of use a value, vcl can retrieve corect values on the fly (to be implemented)
+ const int PB_Mini_Height = 15;
+ const int PB_Norm_Height = 21;
+
+ HIThemeButtonDrawInfo aPushInfo;
+ aPushInfo.version = 0;
+
+ // no animation
+ aPushInfo.animation.time.start = 0;
+ aPushInfo.animation.time.current = 0;
+ PushButtonValue* pPBVal = aValue.getType() == CTRL_PUSHBUTTON ? (PushButtonValue*)&aValue : NULL;
+ int nPaintHeight = static_cast<int>(rc.size.height);
+
+ if( pPBVal && pPBVal->mbBevelButton )
+ {
+ aPushInfo.kind = kThemeRoundedBevelButton;
+ }
+ else if( rc.size.height <= PB_Norm_Height )
+ {
+ aPushInfo.kind = kThemePushButtonMini;
+ nPaintHeight = PB_Mini_Height;
+ }
+ else if( pPBVal->mbSingleLine || rc.size.height < (PB_Norm_Height + PB_Norm_Height/2) )
+ {
+ aPushInfo.kind = kThemePushButtonNormal;
+ nPaintHeight = PB_Norm_Height;
+
+ // avoid clipping when focused
+ rc.origin.x += FOCUS_RING_WIDTH/2;
+ rc.size.width -= FOCUS_RING_WIDTH;
+
+ if( (nState & CTRL_STATE_DEFAULT) != 0 )
+ {
+ AquaBlinker::Blink( mpFrame, buttonRect );
+ // show correct animation phase
+ aPushInfo.animation.time.current = CFAbsoluteTimeGetCurrent();
+ }
+ }
+ else
+ aPushInfo.kind = kThemeBevelButton;
+
+ // translate the origin for controls with fixed paint height
+ // so content ends up somewhere sensible
+ int delta_y = static_cast<int>(rc.size.height) - nPaintHeight;
+ rc.origin.y += delta_y/2;
+
+ aPushInfo.state = getState( nState );
+ aPushInfo.value = ImplGetButtonValue( aValue.getTristateVal() );
+
+ aPushInfo.adornment = (( nState & CTRL_STATE_DEFAULT ) != 0) ?
+ kThemeAdornmentDefault :
+ kThemeAdornmentNone;
+ if( (nState & CTRL_STATE_FOCUSED) != 0 )
+ aPushInfo.adornment |= kThemeAdornmentFocus;
+
+ HIThemeDrawButton( &rc, &aPushInfo, mrContext, kHIThemeOrientationNormal, NULL );
+ bOK = true;
+ }
+ break;
+
+ case CTRL_RADIOBUTTON:
+ case CTRL_CHECKBOX:
+ {
+ HIThemeButtonDrawInfo aInfo;
+ aInfo.version = 0;
+ switch( nType )
+ {
+ case CTRL_RADIOBUTTON: if(rc.size.width >= BUTTON_HEIGHT) aInfo.kind = kThemeRadioButton;
+ else aInfo.kind = kThemeSmallRadioButton;
+ break;
+ case CTRL_CHECKBOX: if(rc.size.width >= BUTTON_HEIGHT) aInfo.kind = kThemeCheckBox;
+ else aInfo.kind = kThemeSmallCheckBox;
+ break;
+ }
+
+ aInfo.state = getState( nState );
+
+ ButtonValue aButtonValue = aValue.getTristateVal();
+ aInfo.value = ImplGetButtonValue( aButtonValue );
+
+ aInfo.adornment = (( nState & CTRL_STATE_DEFAULT ) != 0) ?
+ kThemeAdornmentDefault :
+ kThemeAdornmentNone;
+ if( (nState & CTRL_STATE_FOCUSED) != 0 )
+ aInfo.adornment |= kThemeAdornmentFocus;
+ HIThemeDrawButton( &rc, &aInfo, mrContext, kHIThemeOrientationNormal, NULL );
+ bOK = true;
+ }
+ break;
+
+ case CTRL_LISTNODE:
+ {
+ ButtonValue aButtonValue = aValue.getTristateVal();
+
+ if( Application::GetSettings().GetLayoutRTL() && aButtonValue == BUTTONVALUE_OFF )
+ {
+ // FIXME: a value of kThemeDisclosureLeft
+ // should draw a theme compliant left disclosure triangle
+ // sadly this does not seem to work, so we'll draw a left
+ // grey equilateral triangle here ourselves.
+ // Perhaps some other HIThemeButtonDrawInfo setting would do the trick ?
+
+ CGContextSetShouldAntialias( mrContext, true );
+ CGFloat aGrey[] = { 0.45, 0.45, 0.45, 1.0 };
+ CGContextSetFillColor( mrContext, aGrey );
+ CGContextBeginPath( mrContext );
+ float x = rc.origin.x + rc.size.width;
+ float y = rc.origin.y;
+ CGContextMoveToPoint( mrContext, x, y );
+ y += rc.size.height;
+ CGContextAddLineToPoint( mrContext, x, y );
+ x -= rc.size.height * 0.866; // cos( 30 degree ) is approx. 0.866
+ y -= rc.size.height/2;
+ CGContextAddLineToPoint( mrContext, x, y );
+ CGContextDrawPath( mrContext, kCGPathEOFill );
+ }
+ else
+ {
+ HIThemeButtonDrawInfo aInfo;
+ aInfo.version = 0;
+ aInfo.kind = kThemeDisclosureTriangle;
+ aInfo.value = kThemeDisclosureRight;
+ aInfo.state = getState( nState );
+
+ aInfo.adornment = kThemeAdornmentNone;
+
+ switch( aButtonValue ) {
+ case BUTTONVALUE_ON: aInfo.value = kThemeDisclosureDown;//expanded
+ break;
+ case BUTTONVALUE_OFF:
+ // FIXME: this should have drawn a theme compliant disclosure triangle
+ // (see above)
+ if( Application::GetSettings().GetLayoutRTL() )
+ {
+ aInfo.value = kThemeDisclosureLeft;//collapsed, RTL
+ }
+ break;
+ case BUTTONVALUE_DONTKNOW: //what to do?
+ default:
+ break;
+ }
+
+ HIThemeDrawButton( &rc, &aInfo, mrContext, kHIThemeOrientationNormal, NULL );
+ }
+ bOK = true;
+ }
+ break;
+
+ case CTRL_PROGRESS:
+ case CTRL_INTROPROGRESS:
+ {
+ long nProgressWidth = aValue.getNumericVal();
+ HIThemeTrackDrawInfo aTrackInfo;
+ aTrackInfo.version = 0;
+ aTrackInfo.kind = (rc.size.height > 10) ? kThemeProgressBarLarge : kThemeProgressBarMedium;
+ aTrackInfo.bounds = rc;
+ aTrackInfo.min = 0;
+ aTrackInfo.max = static_cast<SInt32>(rc.size.width);
+ aTrackInfo.value = nProgressWidth;
+ aTrackInfo.reserved = 0;
+ aTrackInfo.bounds.origin.y -= 2; // FIXME: magic for shadow
+ aTrackInfo.bounds.size.width -= 2; // FIXME: magic for shadow
+ aTrackInfo.attributes = kThemeTrackHorizontal;
+ if( Application::GetSettings().GetLayoutRTL() )
+ aTrackInfo.attributes |= kThemeTrackRightToLeft;
+ aTrackInfo.enableState = getTrackState( nState );
+ // the intro bitmap never gets key anyway; we want to draw that enabled
+ if( nType == CTRL_INTROPROGRESS )
+ aTrackInfo.enableState = kThemeTrackActive;
+ aTrackInfo.filler1 = 0;
+ aTrackInfo.trackInfo.progress.phase = static_cast<UInt8>(CFAbsoluteTimeGetCurrent()*10.0);
+
+ HIThemeDrawTrack( &aTrackInfo, NULL, mrContext, kHIThemeOrientationNormal );
+ bOK = true;
+ }
+ break;
+
+ case CTRL_SLIDER:
+ {
+ SliderValue* pSLVal = (SliderValue*)&aValue;
+
+ HIThemeTrackDrawInfo aTrackDraw;
+ aTrackDraw.kind = kThemeSliderMedium;
+ if( nPart == PART_TRACK_HORZ_AREA || nPart == PART_TRACK_VERT_AREA )
+ {
+ aTrackDraw.bounds = rc;
+ aTrackDraw.min = pSLVal->mnMin;
+ aTrackDraw.max = pSLVal->mnMax;
+ aTrackDraw.value = pSLVal->mnCur;
+ aTrackDraw.reserved = 0;
+ aTrackDraw.attributes = kThemeTrackShowThumb;
+ if( nPart == PART_TRACK_HORZ_AREA )
+ aTrackDraw.attributes |= kThemeTrackHorizontal;
+ aTrackDraw.enableState = (nState & CTRL_STATE_ENABLED)
+ ? kThemeTrackActive : kThemeTrackInactive;
+
+ SliderTrackInfo aSlideInfo;
+ aSlideInfo.thumbDir = kThemeThumbUpward;
+ aSlideInfo.pressState = 0;
+ aTrackDraw.trackInfo.slider = aSlideInfo;
+
+ HIThemeDrawTrack( &aTrackDraw, NULL, mrContext, kHIThemeOrientationNormal );
+ bOK = true;
+ }
+ }
+ break;
+
+ case CTRL_SCROLLBAR:
+ {
+ const ScrollbarValue* pScrollbarVal = (aValue.getType() == CTRL_SCROLLBAR) ? static_cast<const ScrollbarValue*>(&aValue) : NULL;
+
+ if( nPart == PART_DRAW_BACKGROUND_VERT ||
+ nPart == PART_DRAW_BACKGROUND_HORZ )
+ {
+ HIThemeTrackDrawInfo aTrackDraw;
+ aTrackDraw.kind = kThemeMediumScrollBar;
+ // FIXME: the scrollbar length must be adjusted
+ if (nPart == PART_DRAW_BACKGROUND_VERT)
+ rc.size.height += 2;
+ else
+ rc.size.width += 2;
+
+ aTrackDraw.bounds = rc;
+ aTrackDraw.min = pScrollbarVal->mnMin;
+ aTrackDraw.max = pScrollbarVal->mnMax - pScrollbarVal->mnVisibleSize;
+ aTrackDraw.value = pScrollbarVal->mnCur;
+ aTrackDraw.reserved = 0;
+ aTrackDraw.attributes = kThemeTrackShowThumb;
+ if( nPart == PART_DRAW_BACKGROUND_HORZ )
+ aTrackDraw.attributes |= kThemeTrackHorizontal;
+ aTrackDraw.enableState = getTrackState( nState );
+
+ ScrollBarTrackInfo aScrollInfo;
+ aScrollInfo.viewsize = pScrollbarVal->mnVisibleSize;
+ aScrollInfo.pressState = 0;
+
+ if ( pScrollbarVal->mnButton1State & CTRL_STATE_ENABLED )
+ {
+ if ( pScrollbarVal->mnButton1State & CTRL_STATE_PRESSED )
+ aScrollInfo.pressState = kThemeTopOutsideArrowPressed;
+ }
+
+ if ( pScrollbarVal->mnButton2State & CTRL_STATE_ENABLED )
+ {
+ if ( pScrollbarVal->mnButton2State & CTRL_STATE_PRESSED )
+ aScrollInfo.pressState = kThemeBottomOutsideArrowPressed;
+ }
+
+ if ( pScrollbarVal->mnThumbState & CTRL_STATE_ENABLED )
+ {
+ if ( pScrollbarVal->mnThumbState & CTRL_STATE_PRESSED )
+ aScrollInfo.pressState = kThemeThumbPressed;
+ }
+
+ aTrackDraw.trackInfo.scrollbar = aScrollInfo;
+
+ HIThemeDrawTrack( &aTrackDraw, NULL, mrContext, kHIThemeOrientationNormal );
+ bOK = true;
+ }
+ }
+ break;
+
+ case CTRL_TAB_PANE:
+ {
+ HIThemeTabPaneDrawInfo aTabPaneDrawInfo;
+ aTabPaneDrawInfo.version = 1;
+ aTabPaneDrawInfo.state = kThemeStateActive;
+ aTabPaneDrawInfo.direction=kThemeTabNorth;
+ aTabPaneDrawInfo.size=kHIThemeTabSizeNormal;
+ aTabPaneDrawInfo.kind=kHIThemeTabKindNormal;
+
+ //the border is outside the rect rc for Carbon
+ //but for VCL it should be inside
+ rc.origin.x+=1;
+ rc.origin.y-=TAB_HEIGHT_NORMAL/2;
+ rc.size.height+=TAB_HEIGHT_NORMAL/2;
+ rc.size.width-=2;
+
+ HIThemeDrawTabPane(&rc, &aTabPaneDrawInfo, mrContext, kHIThemeOrientationNormal);
+
+ bOK = true;
+ }
+ break;
+
+ case CTRL_TAB_ITEM:
+ {
+ HIThemeTabDrawInfo aTabItemDrawInfo;
+ aTabItemDrawInfo.version=1;
+ aTabItemDrawInfo.style=kThemeTabNonFront;
+ aTabItemDrawInfo.direction=kThemeTabNorth;
+ aTabItemDrawInfo.size=kHIThemeTabSizeNormal;
+ aTabItemDrawInfo.adornment=kHIThemeTabAdornmentTrailingSeparator;
+ //State
+ if(nState & CTRL_STATE_SELECTED) {
+ aTabItemDrawInfo.style=kThemeTabFront;
+ }
+ if(nState & CTRL_STATE_FOCUSED) {
+ aTabItemDrawInfo.adornment|=kHIThemeTabAdornmentFocus;
+ }
+
+ //first, last or middle tab
+ aTabItemDrawInfo.position=kHIThemeTabPositionMiddle;
+
+ TabitemValue* pTabValue = (TabitemValue *) &aValue;
+ unsigned int nAlignment = pTabValue->mnAlignment;
+ //TABITEM_LEFTALIGNED (and TABITEM_RIGHTALIGNED) for the leftmost (or rightmost) tab
+ //when there are several lines of tabs because there is only one first tab and one
+ //last tab and TABITEM_FIRST_IN_GROUP (and TABITEM_LAST_IN_GROUP) because when the
+ //line width is different from window width, there may not be TABITEM_RIGHTALIGNED
+ if( ( (nAlignment & TABITEM_LEFTALIGNED)&&(nAlignment & TABITEM_RIGHTALIGNED) ) ||
+ ( (nAlignment & TABITEM_FIRST_IN_GROUP)&&(nAlignment & TABITEM_LAST_IN_GROUP) )
+ ) //tab alone
+ aTabItemDrawInfo.position=kHIThemeTabPositionOnly;
+ else if((nAlignment & TABITEM_LEFTALIGNED)||(nAlignment & TABITEM_FIRST_IN_GROUP))
+ aTabItemDrawInfo.position=kHIThemeTabPositionFirst;
+ else if((nAlignment & TABITEM_RIGHTALIGNED)||(nAlignment & TABITEM_LAST_IN_GROUP))
+ aTabItemDrawInfo.position=kHIThemeTabPositionLast;
+
+ //support for RTL
+ //see issue 79748
+ if( Application::GetSettings().GetLayoutRTL() ) {
+ if( aTabItemDrawInfo.position == kHIThemeTabPositionFirst )
+ aTabItemDrawInfo.position = kHIThemeTabPositionLast;
+ else if( aTabItemDrawInfo.position == kHIThemeTabPositionLast )
+ aTabItemDrawInfo.position = kHIThemeTabPositionFirst;
+ }
+
+ rc.size.width+=2;//because VCL has 2 empty pixels between 2 tabs
+ rc.origin.x-=1;
+
+ HIThemeDrawTab(&rc, &aTabItemDrawInfo, mrContext, kHIThemeOrientationNormal, &rc );
+
+ bOK=true;
+ }
+ break;
+
+ case CTRL_LISTBOX:
+ switch( nPart)
+ {
+ case PART_ENTIRE_CONTROL:
+ case PART_BUTTON_DOWN:
+ {
+ HIThemeButtonDrawInfo aListInfo;
+ aListInfo.version = 0;
+ aListInfo.kind = kThemePopupButton;
+ aListInfo.state = getState( nState );//kThemeStateInactive -> greyed
+ aListInfo.value = kThemeButtonOn;
+
+ aListInfo.adornment = kThemeAdornmentDefault;
+ if( (nState & CTRL_STATE_FOCUSED) != 0 )
+ aListInfo.adornment |= kThemeAdornmentFocus;
+
+ HIThemeDrawButton(&rc, &aListInfo, mrContext, kHIThemeOrientationNormal,&rc);
+ bOK = true;
+ break;
+ }
+ case PART_WINDOW:
+ {
+ HIThemeFrameDrawInfo aTextDrawInfo;
+ aTextDrawInfo.version=0;
+ aTextDrawInfo.kind=kHIThemeFrameTextFieldSquare;
+ aTextDrawInfo.state=getState( nState );
+ aTextDrawInfo.isFocused=false;
+
+ rc.size.width+=1;//else there's a white space because an OS X theme has no 3D border
+ rc.size.height+=1;
+ HIThemeDrawFrame(&rc, &aTextDrawInfo, mrContext, kHIThemeOrientationNormal);
+
+ if(nState & CTRL_STATE_FOCUSED) HIThemeDrawFocusRect(&rc, true, mrContext, kHIThemeOrientationNormal);
+
+ bOK=true;
+ break;
+ }
+ }
+ break;
+
+ case CTRL_EDITBOX:
+ case CTRL_MULTILINE_EDITBOX:
+ {
+ HIThemeFrameDrawInfo aTextDrawInfo;
+ aTextDrawInfo.version=0;
+ aTextDrawInfo.kind=kHIThemeFrameTextFieldSquare;
+ aTextDrawInfo.state=getState( nState );
+ aTextDrawInfo.isFocused=false;
+
+ rc.size.width += 1; // else there may be a white space because an OS X theme has no 3D border
+ // change rc so that the frame will encompass only the content region
+ // see counterpart in GetNativeControlRegion
+ rc.size.width += 2;
+ rc.size.height += 2;
+
+ //CGContextSetFillColorWithColor
+ CGContextFillRect (mrContext, CGRectMake(rc.origin.x, rc.origin.y, rc.size.width, rc.size.height));
+ //fill a white background, because drawFrame only draws the border
+
+ HIThemeDrawFrame(&rc, &aTextDrawInfo, mrContext, kHIThemeOrientationNormal);
+
+ if(nState & CTRL_STATE_FOCUSED) HIThemeDrawFocusRect(&rc, true, mrContext, kHIThemeOrientationNormal);
+
+ bOK=true;
+ }
+ break;
+
+ case CTRL_SPINBOX:
+ {
+ if(nPart == PART_ENTIRE_CONTROL)
+ {
+ //text field:
+ HIThemeFrameDrawInfo aTextDrawInfo;
+ aTextDrawInfo.version=0;
+ aTextDrawInfo.kind=kHIThemeFrameTextFieldSquare;
+ aTextDrawInfo.state=getState( nState );
+ aTextDrawInfo.isFocused=false;
+
+ //rc.size.width contains the full size of the spinbox ie textfield + button
+ //so we remove the button width and the space between the button and the textfield
+ rc.size.width -= SPIN_BUTTON_SPACE + SPIN_BUTTON_WIDTH + 2*FOCUS_RING_WIDTH;
+ rc.origin.x += FOCUS_RING_WIDTH;
+ rc.origin.y += FOCUS_RING_WIDTH;
+
+ //CGContextSetFillColorWithColor
+ CGContextFillRect (mrContext, CGRectMake(rc.origin.x, rc.origin.y, rc.size.width, rc.size.height));
+ //fill a white background, because drawFrame only draws the border
+
+ HIThemeDrawFrame(&rc, &aTextDrawInfo, mrContext, kHIThemeOrientationNormal);
+
+ if(nState & CTRL_STATE_FOCUSED) HIThemeDrawFocusRect(&rc, true, mrContext, kHIThemeOrientationNormal);
+
+ //buttons:
+ const SpinbuttonValue* pSpinButtonVal = (aValue.getType() == CTRL_SPINBUTTONS) ? static_cast<const SpinbuttonValue*>(&aValue) : NULL;
+ ControlState nUpperState = CTRL_STATE_ENABLED;//state of the upper button
+ ControlState nLowerState = CTRL_STATE_ENABLED;//and of the lower button
+ if(pSpinButtonVal) {//pSpinButtonVal is sometimes null
+ nUpperState = (ControlState) pSpinButtonVal->mnUpperState;
+ nLowerState = (ControlState) pSpinButtonVal->mnLowerState;
+
+ HIThemeButtonDrawInfo aSpinInfo;
+ aSpinInfo.kind = kThemeIncDecButton;
+ aSpinInfo.state = kThemeStateActive;
+ if(nUpperState & CTRL_STATE_PRESSED)
+ aSpinInfo.state = kThemeStatePressedUp;
+ else if(nLowerState & CTRL_STATE_PRESSED)
+ aSpinInfo.state = kThemeStatePressedDown;
+ else if((nUpperState & ~CTRL_STATE_ENABLED)||(nLowerState & ~CTRL_STATE_ENABLED))
+ aSpinInfo.state = kThemeStateInactive;
+ else if((nUpperState & CTRL_STATE_ROLLOVER)||(nLowerState & CTRL_STATE_ROLLOVER))
+ aSpinInfo.state = kThemeStateRollover;
+
+ Rectangle aSpinRect( pSpinButtonVal->maUpperRect );
+ aSpinRect.Union( pSpinButtonVal->maLowerRect );
+ HIRect buttonRc = ImplGetHIRectFromRectangle(aSpinRect);
+
+ // FIXME: without this fuzz factor there is some unwanted clipping
+ if( Application::GetSettings().GetLayoutRTL() )
+ buttonRc.origin.x -= FOCUS_RING_WIDTH - CLIP_FUZZ;
+ else
+ buttonRc.origin.x += FOCUS_RING_WIDTH + CLIP_FUZZ;
+
+ switch( aValue.getTristateVal() )
+ {
+ case BUTTONVALUE_ON: aSpinInfo.value = kThemeButtonOn;
+ break;
+ case BUTTONVALUE_OFF: aSpinInfo.value = kThemeButtonOff;
+ break;
+ case BUTTONVALUE_MIXED:
+ case BUTTONVALUE_DONTKNOW:
+ default: aSpinInfo.value = kThemeButtonMixed;
+ break;
+ }
+
+ aSpinInfo.adornment = ( ((nUpperState & CTRL_STATE_DEFAULT) != 0 ) ||
+ ((nLowerState & CTRL_STATE_DEFAULT) != 0 )) ?
+ kThemeAdornmentDefault :
+ kThemeAdornmentNone;
+ if( ((nUpperState & CTRL_STATE_FOCUSED) != 0 ) || ((nLowerState & CTRL_STATE_FOCUSED) != 0 ))
+ aSpinInfo.adornment |= kThemeAdornmentFocus;
+
+ HIThemeDrawButton( &buttonRc, &aSpinInfo, mrContext, kHIThemeOrientationNormal, NULL );
+ }
+
+ bOK=true;
+ }
+
+ }
+ break;
+
+ case CTRL_FRAME:
+ {
+ sal_uInt16 nStyle = aValue.getNumericVal();
+ if( nPart == PART_BORDER ) {
+ if(!( nStyle & FRAME_DRAW_MENU ) && !(nStyle & FRAME_DRAW_WINDOWBORDER) )
+ {
+ // #i84756# strange effects start to happen when HIThemeDrawFrame
+ // meets the border of the window. These can be avoided by clipping
+ // to the boundary of the frame
+ if( rc.origin.y + rc.size.height >= mpFrame->maGeometry.nHeight-3 )
+ {
+ CGMutablePathRef rPath = CGPathCreateMutable();
+ CGPathAddRect( rPath, NULL, CGRectMake( 0, 0, mpFrame->maGeometry.nWidth-1, mpFrame->maGeometry.nHeight-1 ) );
+
+ CGContextBeginPath( mrContext );
+ CGContextAddPath( mrContext, rPath );
+ CGContextClip( mrContext );
+ CGPathRelease( rPath );
+ }
+
+ HIThemeFrameDrawInfo aTextDrawInfo;
+ aTextDrawInfo.version=0;
+ aTextDrawInfo.kind=kHIThemeFrameListBox;
+ aTextDrawInfo.state=kThemeStateActive;
+ aTextDrawInfo.isFocused=false;
+
+ HIThemeDrawFrame(&rc, &aTextDrawInfo, mrContext, kHIThemeOrientationNormal);
+
+ bOK=true;
+ }
+ }
+ }
+ break;
+
+ case CTRL_LISTNET:
+ {
+ //do nothing as there isn't net for listviews on macos
+ bOK=true;
+ }
+ break;
+
+ }
+
+ CGContextRestoreGState( mrContext );
+
+ /* #i90291# in most cases invalidating the whole control region instead
+ of just the unclipped part of it is sufficient (and probably faster).
+ However for the window background we should not unnecessarily enlarge
+ the really changed rectangle since the difference is usually quite high
+ (the background is always drawn as a whole since we don't know anything
+ about its possible contents)
+ */
+ if( nType == CTRL_WINDOW_BACKGROUND )
+ {
+ CGRect aRect = { { 0, 0 }, { 0, 0 } };
+ if( mxClipPath )
+ aRect = CGPathGetBoundingBox( mxClipPath );
+ if( aRect.size.width != 0 && aRect.size.height != 0 )
+ buttonRect.Intersection( Rectangle( Point( static_cast<long int>(aRect.origin.x),
+ static_cast<long int>(aRect.origin.y) ),
+ Size( static_cast<long int>(aRect.size.width),
+ static_cast<long int>(aRect.size.height) ) ) );
+ }
+
+ RefreshRect( buttonRect.Left(), buttonRect.Top(), buttonRect.GetWidth(), buttonRect.GetHeight() );
+
+ return bOK;
+}
+
+/*
+ * GetNativeControlRegion()
+ *
+ * If the return value is sal_True, rNativeBoundingRegion
+ * contains the true bounding region covered by the control
+ * including any adornment, while rNativeContentRegion contains the area
+ * within the control that can be safely drawn into without drawing over
+ * the borders of the control.
+ *
+ * rControlRegion: The bounding region of the control in VCL frame coordinates.
+ * aValue: An optional value (tristate/numerical/string)
+ * aCaption: A caption or title string (like button text etc)
+ */
+sal_Bool AquaSalGraphics::getNativeControlRegion( ControlType nType, ControlPart nPart, const Rectangle& rControlRegion, ControlState /*nState*/,
+ const ImplControlValue& aValue, const OUString&,
+ Rectangle &rNativeBoundingRegion, Rectangle &rNativeContentRegion )
+
+{
+ sal_Bool toReturn = sal_False;
+
+ Rectangle aCtrlBoundRect( rControlRegion );
+ short x = aCtrlBoundRect.Left();
+ short y = aCtrlBoundRect.Top();
+ short w, h;
+
+ sal_uInt8 nBorderCleanup = 0;
+
+ switch (nType)
+ {
+ case CTRL_SLIDER:
+ {
+ if( nPart == PART_THUMB_HORZ )
+ {
+ w = 19; // taken from HIG
+ h = aCtrlBoundRect.GetHeight();
+ rNativeBoundingRegion = rNativeContentRegion = Rectangle( Point( x, y ), Size( w, h ) );
+ toReturn = true;
+ }
+ else if( nPart == PART_THUMB_VERT )
+ {
+ w = aCtrlBoundRect.GetWidth();
+ h = 18; // taken from HIG
+ rNativeBoundingRegion = rNativeContentRegion = Rectangle( Point( x, y ), Size( w, h ) );
+ toReturn = true;
+ }
+ }
+ break;
+
+ case CTRL_SCROLLBAR:
+ {
+ Rectangle aRect;
+ if( AquaGetScrollRect( /* m_nScreen */ nPart, aCtrlBoundRect, aRect ) )
+ {
+ toReturn = sal_True;
+ rNativeBoundingRegion = aRect;
+ rNativeContentRegion = aRect;
+ }
+ }
+ break;
+
+ case CTRL_PUSHBUTTON:
+ case CTRL_RADIOBUTTON:
+ case CTRL_CHECKBOX:
+ {
+ if ( nType == CTRL_PUSHBUTTON )
+ {
+ w = aCtrlBoundRect.GetWidth();
+ h = aCtrlBoundRect.GetHeight();
+ }
+ else
+ {
+ // checkbox and radio borders need cleanup after unchecking them
+ nBorderCleanup = 4;
+
+ // TEXT_SEPARATOR to respect Aqua HIG
+ w = BUTTON_WIDTH + TEXT_SEPARATOR;
+ h = BUTTON_HEIGHT;
+
+ }
+
+ rNativeContentRegion = Rectangle( Point( x, y ), Size( w, h + nBorderCleanup) );
+ rNativeBoundingRegion = Rectangle( Point( x, y ), Size( w, h ) );
+
+ toReturn = sal_True;
+ }
+ break;
+ case CTRL_PROGRESS:
+ {
+ Rectangle aRect( aCtrlBoundRect );
+ if( aRect.GetHeight() < 16 )
+ aRect.Bottom() = aRect.Top() + 9; // values taken from HIG for medium progress
+ else
+ aRect.Bottom() = aRect.Top() + 15; // values taken from HIG for large progress
+ rNativeBoundingRegion = aRect;
+ rNativeContentRegion = aRect;
+ toReturn = sal_True;
+ }
+ break;
+
+ case CTRL_INTROPROGRESS:
+ {
+ Rectangle aRect( aCtrlBoundRect );
+ aRect.Bottom() = aRect.Top() + INTRO_PROGRESS_HEIGHT; // values taken from HIG for medium progress
+ rNativeBoundingRegion = aRect;
+ rNativeContentRegion = aRect;
+ toReturn = sal_True;
+ }
+ break;
+
+ case CTRL_TAB_ITEM:
+
+ w = aCtrlBoundRect.GetWidth() + 2*TAB_TEXT_OFFSET - 2*VCL_TAB_TEXT_OFFSET;
+
+#ifdef OLD_TAB_STYLE
+ h = TAB_HEIGHT_NORMAL;
+#else
+ h = TAB_HEIGHT_NORMAL+2;
+#endif
+ rNativeContentRegion = Rectangle( Point( x, y ), Size( w, h ) );
+ rNativeBoundingRegion = Rectangle( Point( x, y ), Size( w, h ) );
+
+ toReturn = sal_True;
+
+ break;
+
+ case CTRL_EDITBOX:
+ {
+ w = aCtrlBoundRect.GetWidth();
+ if( w < 3+2*FOCUS_RING_WIDTH )
+ w = 3+2*FOCUS_RING_WIDTH;
+ h = TEXT_EDIT_HEIGHT_NORMAL+2*FOCUS_RING_WIDTH;
+ if( h < aCtrlBoundRect.GetHeight() )
+ h = aCtrlBoundRect.GetHeight();
+
+ rNativeContentRegion = Rectangle( Point( x+FOCUS_RING_WIDTH, y+FOCUS_RING_WIDTH ), Size( w-2*(FOCUS_RING_WIDTH+1), h-2*(FOCUS_RING_WIDTH+1) ) );
+ rNativeBoundingRegion = Rectangle( Point( x, y ), Size( w, h ) );
+
+ toReturn = sal_True;
+ }
+ break;
+ case CTRL_LISTBOX:
+ case CTRL_COMBOBOX:
+ {
+ if( nPart == PART_ENTIRE_CONTROL )
+ {
+ w = aCtrlBoundRect.GetWidth();
+ h = COMBOBOX_HEIGHT_NORMAL;//listboxes and comboxes have the same height
+
+ rNativeContentRegion = Rectangle( Point( x+FOCUS_RING_WIDTH, y+FOCUS_RING_WIDTH ), Size( w-2*FOCUS_RING_WIDTH, h ) );
+ rNativeBoundingRegion = Rectangle( Point( x, y ), Size( w, h+2*FOCUS_RING_WIDTH ) );
+
+ toReturn = sal_True;
+ }
+ else if( nPart == PART_BUTTON_DOWN )
+ {
+ w = aCtrlBoundRect.GetWidth();
+ if( w < 3+2*FOCUS_RING_WIDTH )
+ w = 3+2*FOCUS_RING_WIDTH;
+ h = COMBOBOX_HEIGHT_NORMAL;//listboxes and comboxes have the same height
+
+ x += w-DROPDOWN_BUTTON_WIDTH - FOCUS_RING_WIDTH;
+ y += FOCUS_RING_WIDTH;
+ w = DROPDOWN_BUTTON_WIDTH;
+
+ rNativeContentRegion = Rectangle( Point( x, y ), Size( w, h ) );
+ rNativeBoundingRegion = Rectangle( Point( x, y ), Size( w+FOCUS_RING_WIDTH, h+2*FOCUS_RING_WIDTH ) );
+
+ toReturn = true;
+ }
+ else if( nPart == PART_SUB_EDIT )
+ {
+ w = aCtrlBoundRect.GetWidth();
+ h = COMBOBOX_HEIGHT_NORMAL;//listboxes and comboxes have the same height
+
+ x += FOCUS_RING_WIDTH;
+ x += 3; // add an offset for rounded borders
+ y += 2; // don't draw into upper border
+ y += FOCUS_RING_WIDTH;
+ w -= 3 + DROPDOWN_BUTTON_WIDTH + 2*FOCUS_RING_WIDTH;
+ if( nType == CTRL_LISTBOX )
+ w -= 9; // HIG specifies 9 units distance between dropdown button area and content
+ h -= 4; // don't draw into lower border
+
+ rNativeContentRegion = Rectangle( Point( x, y ), Size( w, h ) );
+ rNativeBoundingRegion = Rectangle( Point( x, y ), Size( w+FOCUS_RING_WIDTH, h+2*FOCUS_RING_WIDTH ) );
+
+ toReturn = true;
+ }
+ }
+ break;
+ case CTRL_SPINBOX:
+ if( nPart == PART_ENTIRE_CONTROL ) {
+ w = aCtrlBoundRect.GetWidth();
+ if( w < 3+2*FOCUS_RING_WIDTH+SPIN_BUTTON_SPACE+SPIN_BUTTON_WIDTH )
+ w = 3+2*FOCUS_RING_WIDTH+SPIN_BUTTON_SPACE+SPIN_BUTTON_WIDTH;
+ h = TEXT_EDIT_HEIGHT_NORMAL;
+
+ rNativeContentRegion = Rectangle( Point( x+FOCUS_RING_WIDTH, y ), Size( w-2*FOCUS_RING_WIDTH, h ) );
+ rNativeBoundingRegion = Rectangle( Point( x, y ), Size( w, h+2*FOCUS_RING_WIDTH ) );
+
+ toReturn = sal_True;
+ }
+ else if( nPart == PART_SUB_EDIT ) {
+ w = aCtrlBoundRect.GetWidth() - SPIN_BUTTON_SPACE - SPIN_BUTTON_WIDTH;
+ h = TEXT_EDIT_HEIGHT_NORMAL;
+ x += 4; // add an offset for rounded borders
+ y += 2; // don't draw into upper border
+ w -= 8; // offset for left and right rounded border
+ h -= 4; // don't draw into upper or ower border
+
+ rNativeContentRegion = Rectangle( Point( x + FOCUS_RING_WIDTH, y + FOCUS_RING_WIDTH ), Size( w - 2* FOCUS_RING_WIDTH, h ) );
+ rNativeBoundingRegion = Rectangle( Point( x, y ), Size( w, h+2*FOCUS_RING_WIDTH ) );
+
+ toReturn = sal_True;
+ }
+ else if( nPart == PART_BUTTON_UP ) {
+ //aCtrlBoundRect.GetWidth() contains the width of the full control
+ //ie the width of the textfield + button
+ //x is the position of the left corner of the full control
+ x += aCtrlBoundRect.GetWidth() - SPIN_BUTTON_WIDTH - SPIN_BUTTON_SPACE - CLIP_FUZZ;
+ y += FOCUS_RING_WIDTH - CLIP_FUZZ;
+ w = SPIN_BUTTON_WIDTH + 2*CLIP_FUZZ;
+ h = SPIN_UPPER_BUTTON_HEIGHT + 2*CLIP_FUZZ;
+
+ rNativeContentRegion = Rectangle( Point( x, y ), Size( w, h ) );
+ rNativeBoundingRegion = Rectangle( Point( x, y ), Size( w, h ) );
+
+ toReturn = sal_True;
+ }
+ else if( nPart == PART_BUTTON_DOWN ) {
+ x += aCtrlBoundRect.GetWidth() - SPIN_BUTTON_WIDTH - SPIN_BUTTON_SPACE - CLIP_FUZZ;
+ y += SPIN_UPPER_BUTTON_HEIGHT + FOCUS_RING_WIDTH - CLIP_FUZZ;
+ w = SPIN_BUTTON_WIDTH + 2*CLIP_FUZZ;
+ h = SPIN_LOWER_BUTTON_HEIGHT + 2*CLIP_FUZZ;
+
+ rNativeContentRegion = Rectangle( Point( x, y ), Size( w, h ) );
+ rNativeBoundingRegion = Rectangle( Point( x, y ), Size( w, h ) );
+
+ toReturn = sal_True;
+ }
+ break;
+ case CTRL_FRAME:
+ {
+ sal_uInt16 nStyle = aValue.getNumericVal();
+ if( ( nPart == PART_BORDER ) &&
+ !( nStyle & (FRAME_DRAW_MENU | FRAME_DRAW_WINDOWBORDER | FRAME_DRAW_BORDERWINDOWBORDER) ) )
+ {
+ Rectangle aRect(aCtrlBoundRect);
+ if( nStyle & FRAME_DRAW_DOUBLEIN )
+ {
+ aRect.Left() += 1;
+ aRect.Top() += 1;
+ //rRect.Right() -= 1;
+ //rRect.Bottom() -= 1;
+ }
+ else
+ {
+ aRect.Left() += 1;
+ aRect.Top() += 1;
+ aRect.Right() -= 1;
+ aRect.Bottom() -= 1;
+ }
+
+ rNativeContentRegion = aRect;
+ rNativeBoundingRegion = aRect;
+
+ toReturn = sal_True;
+ }
+ }
+ break;
+
+ case CTRL_MENUBAR:
+ case CTRL_MENU_POPUP:
+ {
+ if(( nPart == PART_MENU_ITEM_CHECK_MARK )||( nPart == PART_MENU_ITEM_RADIO_MARK )) {
+
+ w=10;
+ h=10;//dimensions of the mark (10px font)
+
+ rNativeContentRegion = Rectangle( Point( x, y ), Size( w, h ) );
+ rNativeBoundingRegion = Rectangle( Point( x, y ), Size( w, h ) );
+
+ toReturn = sal_True;
+ }
+ }
+ break;
+
+ }
+
+ return toReturn;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/salnsmenu.mm b/vcl/osx/salnsmenu.mm
new file mode 100644
index 000000000000..796b05ca7ae4
--- /dev/null
+++ b/vcl/osx/salnsmenu.mm
@@ -0,0 +1,207 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/salinst.h"
+#include "osx/saldata.hxx"
+#include "osx/salframe.h"
+#include "osx/salmenu.h"
+#include "osx/salnsmenu.h"
+
+#include "vcl/window.hxx"
+
+@implementation SalNSMenu
+-(id)initWithMenu: (AquaSalMenu*)pMenu
+{
+ mpMenu = pMenu;
+ return [super initWithTitle: [NSString string]];
+}
+
+-(void)menuNeedsUpdate: (NSMenu*)pMenu
+{
+ (void)pMenu;
+ YIELD_GUARD;
+
+ if( mpMenu )
+ {
+ const AquaSalFrame* pFrame = mpMenu->getFrame();
+ if( pFrame && AquaSalFrame::isAlive( pFrame ) )
+ {
+ SalMenuEvent aMenuEvt;
+ aMenuEvt.mnId = 0;
+ aMenuEvt.mpMenu = mpMenu->mpVCLMenu;
+ if( aMenuEvt.mpMenu )
+ {
+ pFrame->CallCallback(SALEVENT_MENUACTIVATE, &aMenuEvt);
+ pFrame->CallCallback(SALEVENT_MENUDEACTIVATE, &aMenuEvt);
+ }
+ else
+ OSL_FAIL( "unconnected menu" );
+ }
+ }
+}
+
+-(void)setSalMenu: (AquaSalMenu*)pMenu
+{
+ mpMenu = pMenu;
+}
+@end
+
+@implementation SalNSMenuItem
+-(id)initWithMenuItem: (AquaSalMenuItem*)pMenuItem
+{
+ mpMenuItem = pMenuItem;
+ id ret = [super initWithTitle: [NSString string]
+ action: @selector(menuItemTriggered:)
+ keyEquivalent: [NSString string]];
+ [ret setTarget: self];
+ return ret;
+}
+-(void)menuItemTriggered: (id)aSender
+{
+ (void)aSender;
+ YIELD_GUARD;
+
+ const AquaSalFrame* pFrame = mpMenuItem->mpParentMenu ? mpMenuItem->mpParentMenu->getFrame() : NULL;
+ if( pFrame && AquaSalFrame::isAlive( pFrame ) && ! pFrame->GetWindow()->IsInModalMode() )
+ {
+ SalMenuEvent aMenuEvt( mpMenuItem->mnId, mpMenuItem->mpVCLMenu );
+ pFrame->CallCallback(SALEVENT_MENUCOMMAND, &aMenuEvt);
+ }
+ else if( mpMenuItem->mpVCLMenu )
+ {
+ // if an item from submenu was selected. the corresponding Window does not exist because
+ // we use native popup menus, so we have to set the selected menuitem directly
+ // incidentally this of course works for top level popup menus, too
+ PopupMenu * pPopupMenu = dynamic_cast<PopupMenu *>(mpMenuItem->mpVCLMenu);
+ if( pPopupMenu )
+ {
+ // FIXME: revise this ugly code
+
+ // select handlers in vcl are dispatch on the original menu
+ // if not consumed by the select handler of the current menu
+ // however since only the starting menu ever came into Execute
+ // the hierarchy is not build up. Workaround this by getting
+ // the menu it should have been
+
+ // get started from hierarchy in vcl menus
+ AquaSalMenu* pParentMenu = mpMenuItem->mpParentMenu;
+ Menu* pCurMenu = mpMenuItem->mpVCLMenu;
+ while( pParentMenu && pParentMenu->mpVCLMenu )
+ {
+ pCurMenu = pParentMenu->mpVCLMenu;
+ pParentMenu = pParentMenu->mpParentSalMenu;
+ }
+
+ pPopupMenu->SetSelectedEntry( mpMenuItem->mnId );
+ pPopupMenu->ImplSelectWithStart( pCurMenu );
+ }
+ else
+ OSL_FAIL( "menubar item without frame !" );
+ }
+}
+@end
+
+@implementation OOStatusItemView
+-(void)drawRect: (NSRect)aRect
+{
+ NSGraphicsContext* pContext = [NSGraphicsContext currentContext];
+ [pContext saveGraphicsState];
+ [SalData::getStatusItem() drawStatusBarBackgroundInRect: aRect withHighlight: NO];
+ if( AquaSalMenu::pCurrentMenuBar )
+ {
+ const std::vector< AquaSalMenu::MenuBarButtonEntry >& rButtons( AquaSalMenu::pCurrentMenuBar->getButtons() );
+ NSRect aFrame = [self frame];
+ NSRect aImgRect = { { 2, 0 }, { 0, 0 } };
+ for( size_t i = 0; i < rButtons.size(); ++i )
+ {
+ NSRect aFromRect = { { 0, 0 },
+ { static_cast<CGFloat>(rButtons[i].maButton.maImage.GetSizePixel().Width()),
+ static_cast<CGFloat>(rButtons[i].maButton.maImage.GetSizePixel().Height()) } };
+ aImgRect.origin.y = floor((aFrame.size.height - aFromRect.size.height)/2);
+ aImgRect.size = aFromRect.size;
+ if( rButtons[i].mpNSImage )
+ [rButtons[i].mpNSImage drawInRect: aImgRect fromRect: aFromRect operation: NSCompositeSourceOver fraction: 1.0];
+ aImgRect.origin.x += aFromRect.size.width + 2;
+ }
+ }
+ [pContext restoreGraphicsState];
+}
+
+-(void)mouseUp: (NSEvent *)pEvent
+{
+ /* check if button goes up inside one of our status buttons */
+ if( AquaSalMenu::pCurrentMenuBar )
+ {
+ const std::vector< AquaSalMenu::MenuBarButtonEntry >& rButtons( AquaSalMenu::pCurrentMenuBar->getButtons() );
+ NSRect aFrame = [self frame];
+ NSRect aImgRect = { { 2, 0 }, { 0, 0 } };
+ NSPoint aMousePt = [pEvent locationInWindow];
+ for( size_t i = 0; i < rButtons.size(); ++i )
+ {
+ NSRect aFromRect = { { 0, 0 },
+ { static_cast<CGFloat>(rButtons[i].maButton.maImage.GetSizePixel().Width()),
+ static_cast<CGFloat>(rButtons[i].maButton.maImage.GetSizePixel().Height()) } };
+ aImgRect.origin.y = (aFrame.size.height - aFromRect.size.height)/2;
+ aImgRect.size = aFromRect.size;
+ if( aMousePt.x >= aImgRect.origin.x && aMousePt.x <= (aImgRect.origin.x+aImgRect.size.width) &&
+ aMousePt.y >= aImgRect.origin.y && aMousePt.y <= (aImgRect.origin.y+aImgRect.size.height) )
+ {
+ if( AquaSalMenu::pCurrentMenuBar->mpFrame && AquaSalFrame::isAlive( AquaSalMenu::pCurrentMenuBar->mpFrame ) )
+ {
+ SalMenuEvent aMenuEvt( rButtons[i].maButton.mnId, AquaSalMenu::pCurrentMenuBar->mpVCLMenu );
+ AquaSalMenu::pCurrentMenuBar->mpFrame->CallCallback(SALEVENT_MENUBUTTONCOMMAND, &aMenuEvt);
+ }
+ return;
+ }
+
+ aImgRect.origin.x += aFromRect.size.width + 2;
+ }
+ }
+}
+
+-(void)layout
+{
+ NSStatusBar* pStatBar = [NSStatusBar systemStatusBar];
+ NSSize aSize = { 0, [pStatBar thickness] };
+ [self removeAllToolTips];
+ if( AquaSalMenu::pCurrentMenuBar )
+ {
+ const std::vector< AquaSalMenu::MenuBarButtonEntry >& rButtons( AquaSalMenu::pCurrentMenuBar->getButtons() );
+ if( ! rButtons.empty() )
+ {
+ aSize.width = 2;
+ for( size_t i = 0; i < rButtons.size(); ++i )
+ {
+ NSRect aImgRect = { { static_cast<CGFloat>(aSize.width),
+ static_cast<CGFloat>(floor((aSize.height-rButtons[i].maButton.maImage.GetSizePixel().Height())/2)) },
+ { static_cast<CGFloat>(rButtons[i].maButton.maImage.GetSizePixel().Width()),
+ static_cast<CGFloat>(rButtons[i].maButton.maImage.GetSizePixel().Height()) } };
+ if( rButtons[i].mpToolTipString )
+ [self addToolTipRect: aImgRect owner: rButtons[i].mpToolTipString userData: NULL];
+ aSize.width += 2 + aImgRect.size.width;
+ }
+ }
+ }
+ [self setFrameSize: aSize];
+}
+@end
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/salnstimer.mm b/vcl/osx/salnstimer.mm
new file mode 100644
index 000000000000..bad2f4e51ff7
--- /dev/null
+++ b/vcl/osx/salnstimer.mm
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/saltimer.h"
+#include "osx/salnstimer.h"
+#include "osx/salinst.h"
+#include "osx/saldata.hxx"
+
+#include "svdata.hxx"
+
+@implementation TimerCallbackCaller
+-(void)timerElapsed:(NSTimer*)pTimer
+{
+ (void)pTimer;
+ ImplSVData* pSVData = ImplGetSVData();
+ if( AquaSalTimer::bDispatchTimer )
+ {
+ if( pSVData->mpSalTimer )
+ {
+ YIELD_GUARD;
+ pSVData->mpSalTimer->CallCallback();
+
+ // NSTimer does not end nextEventMatchingMask of NSApplication
+ // so we need to wakeup a waiting Yield to inform it something happened
+ GetSalData()->mpFirstInstance->wakeupYield();
+ }
+ }
+}
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/salobj.cxx b/vcl/osx/salobj.cxx
new file mode 100644
index 000000000000..16a56b5261ca
--- /dev/null
+++ b/vcl/osx/salobj.cxx
@@ -0,0 +1,206 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <string.h>
+
+#include "osx/saldata.hxx"
+#include "osx/salobj.h"
+#include "osx/salframe.h"
+
+// =======================================================================
+
+AquaSalObject::AquaSalObject( AquaSalFrame* pFrame ) :
+ mpFrame( pFrame ),
+ mnClipX( -1 ),
+ mnClipY( -1 ),
+ mnClipWidth( -1 ),
+ mnClipHeight( -1 ),
+ mbClip( false ),
+ mnX( 0 ),
+ mnY( 0 ),
+ mnWidth( 20 ),
+ mnHeight( 20 )
+{
+ maSysData.nSize = sizeof( maSysData );
+ maSysData.mpNSView = NULL;
+
+ NSRect aInitFrame = { { 0, 0 }, { 20, 20 } };
+ mpClipView = [[NSClipView alloc] initWithFrame: aInitFrame ];
+ if( mpClipView )
+ {
+ [mpFrame->getNSView() addSubview: mpClipView];
+ [mpClipView setHidden: YES];
+ }
+ maSysData.mpNSView = [[NSView alloc] initWithFrame: aInitFrame];
+ if( maSysData.mpNSView )
+ {
+ if( mpClipView )
+ [mpClipView setDocumentView: maSysData.mpNSView];
+ }
+}
+
+// -----------------------------------------------------------------------
+
+AquaSalObject::~AquaSalObject()
+{
+ if( maSysData.mpNSView )
+ {
+ NSView *pView = maSysData.mpNSView;
+ [pView removeFromSuperview];
+ [pView release];
+ }
+ if( mpClipView )
+ {
+ [mpClipView removeFromSuperview];
+ [mpClipView release];
+ }
+}
+
+/*
+ sadly there seems to be no way to impose clipping on a child view,
+ especially a QTMovieView which seems to ignore the current context
+ completely. Also there is no real way to shape a window; on Aqua a
+ similar effect to non-rectangular windows is achieved by using a
+ non-opaque window and not painting where one wants the background
+ to shine through.
+
+ With respect to SalObject this leaves us to having an NSClipView
+ containing the child view. Even a QTMovieView respects the boundaries of
+ that, which gives us a clip "region" consisting of one rectangle.
+ This is gives us an 80% solution only, though.
+*/
+
+// -----------------------------------------------------------------------
+
+void AquaSalObject::ResetClipRegion()
+{
+ mbClip = false;
+ setClippedPosSize();
+}
+
+// -----------------------------------------------------------------------
+
+sal_uInt16 AquaSalObject::GetClipRegionType()
+{
+ return SAL_OBJECT_CLIP_INCLUDERECTS;
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalObject::BeginSetClipRegion( sal_uLong )
+{
+ mbClip = false;
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalObject::UnionClipRegion( long nX, long nY, long nWidth, long nHeight )
+{
+ if( mbClip )
+ {
+ if( nX < mnClipX )
+ {
+ mnClipWidth += mnClipX - nX;
+ mnClipX = nX;
+ }
+ if( nX + nWidth > mnClipX + mnClipWidth )
+ mnClipWidth = nX + nWidth - mnClipX;
+ if( nY < mnClipY )
+ {
+ mnClipHeight += mnClipY - nY;
+ mnClipY = nY;
+ }
+ if( nY + nHeight > mnClipY + mnClipHeight )
+ mnClipHeight = nY + nHeight - mnClipY;
+ }
+ else
+ {
+ mnClipX = nX;
+ mnClipY = nY;
+ mnClipWidth = nWidth;
+ mnClipHeight = nHeight;
+ mbClip = true;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalObject::EndSetClipRegion()
+{
+ setClippedPosSize();
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalObject::SetPosSize( long nX, long nY, long nWidth, long nHeight )
+{
+ mnX = nX;
+ mnY = nY;
+ mnWidth = nWidth;
+ mnHeight = nHeight;
+ setClippedPosSize();
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalObject::setClippedPosSize()
+{
+ NSRect aViewRect = { { 0, 0 }, { static_cast<CGFloat>(mnWidth), static_cast<CGFloat>(mnHeight) } };
+ if( maSysData.mpNSView )
+ {
+ NSView* pNSView = maSysData.mpNSView;
+ [pNSView setFrame: aViewRect];
+ }
+
+ NSRect aClipViewRect = { { static_cast<CGFloat>(mnX), static_cast<CGFloat>(mnY) }, { static_cast<CGFloat>(mnWidth), static_cast<CGFloat>(mnHeight) } };
+ NSPoint aClipPt = { 0, 0 };
+ if( mbClip )
+ {
+ aClipViewRect.origin.x += mnClipX;
+ aClipViewRect.origin.y += mnClipY;
+ aClipViewRect.size.width = mnClipWidth;
+ aClipViewRect.size.height = mnClipHeight;
+ aClipPt.x = mnClipX;
+ if( mnClipY == 0 )
+ aClipPt.y = mnHeight - mnClipHeight;
+ }
+
+ mpFrame->VCLToCocoa( aClipViewRect, false );
+ [mpClipView setFrame: aClipViewRect];
+
+ [mpClipView scrollToPoint: aClipPt];
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalObject::Show( sal_Bool bVisible )
+{
+ if( mpClipView )
+ [mpClipView setHidden: (bVisible ? NO : YES)];
+}
+
+// -----------------------------------------------------------------------
+
+const SystemEnvData* AquaSalObject::GetSystemData() const
+{
+ return &maSysData;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/salprn.cxx b/vcl/osx/salprn.cxx
new file mode 100644
index 000000000000..082fa1cdc199
--- /dev/null
+++ b/vcl/osx/salprn.cxx
@@ -0,0 +1,755 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <boost/bind.hpp>
+
+#include "officecfg/Office/Common.hxx"
+
+#include "vcl/print.hxx"
+#include <sal/macros.h>
+
+#include "osx/salinst.h"
+#include "osx/salprn.h"
+#include "osx/printview.h"
+#include "quartz/salgdi.h"
+#include "osx/saldata.hxx"
+#include "quartz/utils.h"
+
+#include "jobset.h"
+#include "salptype.hxx"
+
+#include "com/sun/star/beans/PropertyValue.hpp"
+#include "com/sun/star/awt/Size.hpp"
+#include "com/sun/star/uno/Sequence.hxx"
+
+#include <algorithm>
+
+using namespace vcl;
+using namespace com::sun::star;
+using namespace com::sun::star::beans;
+
+
+// =======================================================================
+
+AquaSalInfoPrinter::AquaSalInfoPrinter( const SalPrinterQueueInfo& i_rQueue ) :
+ mpGraphics( 0 ),
+ mbGraphics( false ),
+ mbJob( false ),
+ mpPrinter( nil ),
+ mpPrintInfo( nil ),
+ mePageOrientation( ORIENTATION_PORTRAIT ),
+ mnStartPageOffsetX( 0 ),
+ mnStartPageOffsetY( 0 ),
+ mnCurPageRangeStart( 0 ),
+ mnCurPageRangeCount( 0 )
+{
+ NSString* pStr = CreateNSString( i_rQueue.maPrinterName );
+ mpPrinter = [NSPrinter printerWithName: pStr];
+ [pStr release];
+
+ NSPrintInfo* pShared = [NSPrintInfo sharedPrintInfo];
+ if( pShared )
+ {
+ mpPrintInfo = [pShared copy];
+ [mpPrintInfo setPrinter: mpPrinter];
+ mePageOrientation = ([mpPrintInfo orientation] == NSLandscapeOrientation) ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
+#if MACOSX_SDK_VERSION >= 1090
+ [mpPrintInfo setOrientation: NSPaperOrientationPortrait];
+#else
+ [mpPrintInfo setOrientation: NSPortraitOrientation];
+#endif
+ }
+
+ mpGraphics = new AquaSalGraphics();
+
+ const int nWidth = 100, nHeight = 100;
+ maContextMemory.reset( reinterpret_cast<sal_uInt8*>( rtl_allocateMemory( nWidth * 4 * nHeight ) ),
+ boost::bind( rtl_freeMemory, _1 ) );
+
+ if( maContextMemory )
+ {
+ mrContext = CGBitmapContextCreate( maContextMemory.get(), nWidth, nHeight, 8, nWidth * 4, GetSalData()->mxRGBSpace, kCGImageAlphaNoneSkipFirst );
+ if( mrContext )
+ SetupPrinterGraphics( mrContext );
+ }
+}
+
+// -----------------------------------------------------------------------
+
+AquaSalInfoPrinter::~AquaSalInfoPrinter()
+{
+ delete mpGraphics;
+ if( mpPrintInfo )
+ [mpPrintInfo release];
+ if( mrContext )
+ CFRelease( mrContext );
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalInfoPrinter::SetupPrinterGraphics( CGContextRef i_rContext ) const
+{
+ if( mpGraphics )
+ {
+ if( mpPrintInfo )
+ {
+ // FIXME: get printer resolution
+ long nDPIX = 720, nDPIY = 720;
+ NSSize aPaperSize = [mpPrintInfo paperSize];
+
+ NSRect aImageRect = [mpPrintInfo imageablePageBounds];
+ if( mePageOrientation == ORIENTATION_PORTRAIT )
+ {
+ // move mirrored CTM back into paper
+ double dX = 0, dY = aPaperSize.height;
+ // move CTM to reflect imageable area
+ dX += aImageRect.origin.x;
+ dY -= aPaperSize.height - aImageRect.size.height - aImageRect.origin.y;
+ CGContextTranslateCTM( i_rContext, dX + mnStartPageOffsetX, dY - mnStartPageOffsetY );
+ // scale to be top/down and reflect our "virtual" DPI
+ CGContextScaleCTM( i_rContext, 72.0/double(nDPIX), -(72.0/double(nDPIY)) );
+ }
+ else
+ {
+ // move CTM to reflect imageable area
+ double dX = aImageRect.origin.x, dY = aPaperSize.height - aImageRect.size.height - aImageRect.origin.y;
+ CGContextTranslateCTM( i_rContext, -dX, -dY );
+ // turn by 90 degree
+ CGContextRotateCTM( i_rContext, M_PI/2 );
+ // move turned CTM back into paper
+ dX = aPaperSize.height;
+ dY = -aPaperSize.width;
+ CGContextTranslateCTM( i_rContext, dX + mnStartPageOffsetY, dY - mnStartPageOffsetX );
+ // scale to be top/down and reflect our "virtual" DPI
+ CGContextScaleCTM( i_rContext, -(72.0/double(nDPIY)), (72.0/double(nDPIX)) );
+ }
+ mpGraphics->SetPrinterGraphics( i_rContext, nDPIX, nDPIY, 1.0 );
+ }
+ else
+ OSL_FAIL( "no print info in SetupPrinterGraphics" );
+ }
+}
+
+// -----------------------------------------------------------------------
+
+SalGraphics* AquaSalInfoPrinter::GetGraphics()
+{
+ SalGraphics* pGraphics = mbGraphics ? NULL : mpGraphics;
+ mbGraphics = true;
+ return pGraphics;
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalInfoPrinter::ReleaseGraphics( SalGraphics* )
+{
+ mbGraphics = false;
+}
+
+// -----------------------------------------------------------------------
+
+sal_Bool AquaSalInfoPrinter::Setup( SalFrame*, ImplJobSetup* )
+{
+ return sal_False;
+}
+
+// -----------------------------------------------------------------------
+
+sal_Bool AquaSalInfoPrinter::SetPrinterData( ImplJobSetup* io_pSetupData )
+{
+ // FIXME: implement driver data
+ if( io_pSetupData && io_pSetupData->mpDriverData )
+ return SetData( ~0, io_pSetupData );
+
+
+ sal_Bool bSuccess = sal_True;
+
+ // set system type
+ io_pSetupData->mnSystem = JOBSETUP_SYSTEM_MAC;
+
+ // get paper format
+ if( mpPrintInfo )
+ {
+ NSSize aPaperSize = [mpPrintInfo paperSize];
+ double width = aPaperSize.width, height = aPaperSize.height;
+ // set paper
+ PaperInfo aInfo( PtTo10Mu( width ), PtTo10Mu( height ) );
+ aInfo.doSloppyFit();
+ io_pSetupData->mePaperFormat = aInfo.getPaper();
+ if( io_pSetupData->mePaperFormat == PAPER_USER )
+ {
+ io_pSetupData->mnPaperWidth = PtTo10Mu( width );
+ io_pSetupData->mnPaperHeight = PtTo10Mu( height );
+ }
+ else
+ {
+ io_pSetupData->mnPaperWidth = 0;
+ io_pSetupData->mnPaperHeight = 0;
+ }
+
+ // set orientation
+ io_pSetupData->meOrientation = mePageOrientation;
+
+ io_pSetupData->mnPaperBin = 0;
+ io_pSetupData->mpDriverData = reinterpret_cast<sal_uInt8*>(rtl_allocateMemory( 4 ));
+ io_pSetupData->mnDriverDataLen = 4;
+ }
+ else
+ bSuccess = sal_False;
+
+
+ return bSuccess;
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalInfoPrinter::setPaperSize( long i_nWidth, long i_nHeight, Orientation i_eSetOrientation )
+{
+
+ Orientation ePaperOrientation = ORIENTATION_PORTRAIT;
+ const PaperInfo* pPaper = matchPaper( i_nWidth, i_nHeight, ePaperOrientation );
+
+ if( pPaper )
+ {
+ NSString* pPaperName = [CreateNSString( OStringToOUString(PaperInfo::toPSName(pPaper->getPaper()), RTL_TEXTENCODING_ASCII_US) ) autorelease];
+ [mpPrintInfo setPaperName: pPaperName];
+ }
+ else if( i_nWidth > 0 && i_nHeight > 0 )
+ {
+ NSSize aPaperSize = { static_cast<CGFloat>(TenMuToPt(i_nWidth)), static_cast<CGFloat>(TenMuToPt(i_nHeight)) };
+ [mpPrintInfo setPaperSize: aPaperSize];
+ }
+ // this seems counterintuitive
+ mePageOrientation = i_eSetOrientation;
+}
+
+// -----------------------------------------------------------------------
+
+sal_Bool AquaSalInfoPrinter::SetData( sal_uLong i_nFlags, ImplJobSetup* io_pSetupData )
+{
+ if( ! io_pSetupData || io_pSetupData->mnSystem != JOBSETUP_SYSTEM_MAC )
+ return sal_False;
+
+
+ if( mpPrintInfo )
+ {
+ if( (i_nFlags & SAL_JOBSET_ORIENTATION) != 0 )
+ mePageOrientation = io_pSetupData->meOrientation;
+
+ if( (i_nFlags & SAL_JOBSET_PAPERSIZE) != 0)
+ {
+ // set paper format
+ long width = 21000, height = 29700;
+ if( io_pSetupData->mePaperFormat == PAPER_USER )
+ {
+ // #i101108# sanity check
+ if( io_pSetupData->mnPaperWidth && io_pSetupData->mnPaperHeight )
+ {
+ width = io_pSetupData->mnPaperWidth;
+ height = io_pSetupData->mnPaperHeight;
+ }
+ }
+ else
+ {
+ PaperInfo aInfo( io_pSetupData->mePaperFormat );
+ width = aInfo.getWidth();
+ height = aInfo.getHeight();
+ }
+
+ setPaperSize( width, height, mePageOrientation );
+ }
+ }
+
+ return mpPrintInfo != nil;
+}
+
+// -----------------------------------------------------------------------
+
+sal_uLong AquaSalInfoPrinter::GetPaperBinCount( const ImplJobSetup* )
+{
+ return 0;
+}
+
+// -----------------------------------------------------------------------
+
+OUString AquaSalInfoPrinter::GetPaperBinName( const ImplJobSetup*, sal_uLong )
+{
+ return OUString();
+}
+
+// -----------------------------------------------------------------------
+
+sal_uLong AquaSalInfoPrinter::GetCapabilities( const ImplJobSetup*, sal_uInt16 i_nType )
+{
+ switch( i_nType )
+ {
+ case PRINTER_CAPABILITIES_SUPPORTDIALOG:
+ return 0;
+ case PRINTER_CAPABILITIES_COPIES:
+ return 0xffff;
+ case PRINTER_CAPABILITIES_COLLATECOPIES:
+ return 0xffff;
+ case PRINTER_CAPABILITIES_SETORIENTATION:
+ return 1;
+ case PRINTER_CAPABILITIES_SETDUPLEX:
+ return 0;
+ case PRINTER_CAPABILITIES_SETPAPERBIN:
+ return 0;
+ case PRINTER_CAPABILITIES_SETPAPERSIZE:
+ return 1;
+ case PRINTER_CAPABILITIES_SETPAPER:
+ return 1;
+ case PRINTER_CAPABILITIES_EXTERNALDIALOG:
+ return officecfg::Office::Common::Misc::UseSystemPrintDialog::get()
+ ? 1 : 0;
+ case PRINTER_CAPABILITIES_PDF:
+ return 1;
+ case PRINTER_CAPABILITIES_USEPULLMODEL:
+ return 1;
+ default: break;
+ };
+ return 0;
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalInfoPrinter::GetPageInfo( const ImplJobSetup*,
+ long& o_rOutWidth, long& o_rOutHeight,
+ long& o_rPageOffX, long& o_rPageOffY,
+ long& o_rPageWidth, long& o_rPageHeight )
+{
+ if( mpPrintInfo )
+ {
+ sal_Int32 nDPIX = 72, nDPIY = 72;
+ mpGraphics->GetResolution( nDPIX, nDPIY );
+ const double fXScaling = static_cast<double>(nDPIX)/72.0,
+ fYScaling = static_cast<double>(nDPIY)/72.0;
+
+ NSSize aPaperSize = [mpPrintInfo paperSize];
+ o_rPageWidth = static_cast<long>( double(aPaperSize.width) * fXScaling );
+ o_rPageHeight = static_cast<long>( double(aPaperSize.height) * fYScaling );
+
+ NSRect aImageRect = [mpPrintInfo imageablePageBounds];
+ o_rPageOffX = static_cast<long>( aImageRect.origin.x * fXScaling );
+ o_rPageOffY = static_cast<long>( (aPaperSize.height - aImageRect.size.height - aImageRect.origin.y) * fYScaling );
+ o_rOutWidth = static_cast<long>( aImageRect.size.width * fXScaling );
+ o_rOutHeight = static_cast<long>( aImageRect.size.height * fYScaling );
+
+ if( mePageOrientation == ORIENTATION_LANDSCAPE )
+ {
+ std::swap( o_rOutWidth, o_rOutHeight );
+ std::swap( o_rPageWidth, o_rPageHeight );
+ std::swap( o_rPageOffX, o_rPageOffY );
+ }
+ }
+}
+
+static Size getPageSize( vcl::PrinterController& i_rController, sal_Int32 i_nPage )
+{
+ Size aPageSize;
+ uno::Sequence< PropertyValue > aPageParms( i_rController.getPageParameters( i_nPage ) );
+ for( sal_Int32 nProperty = 0, nPropertyCount = aPageParms.getLength(); nProperty < nPropertyCount; ++nProperty )
+ {
+ if ( aPageParms[ nProperty ].Name == "PageSize" )
+ {
+ awt::Size aSize;
+ aPageParms[ nProperty].Value >>= aSize;
+ aPageSize.Width() = aSize.Width;
+ aPageSize.Height() = aSize.Height;
+ break;
+ }
+ }
+ return aPageSize;
+}
+
+sal_Bool AquaSalInfoPrinter::StartJob( const OUString* i_pFileName,
+ const OUString& i_rJobName,
+ const OUString& /*i_rAppName*/,
+ ImplJobSetup* i_pSetupData,
+ vcl::PrinterController& i_rController
+ )
+{
+ if( mbJob )
+ return sal_False;
+
+ sal_Bool bSuccess = sal_False;
+ bool bWasAborted = false;
+ AquaSalInstance* pInst = GetSalData()->mpFirstInstance;
+ PrintAccessoryViewState aAccViewState;
+ sal_Int32 nAllPages = 0;
+
+ // reset IsLastPage
+ i_rController.setLastPage( sal_False );
+
+ // update job data
+ if( i_pSetupData )
+ SetData( ~0, i_pSetupData );
+
+ // do we want a progress panel ?
+ sal_Bool bShowProgressPanel = sal_True;
+ beans::PropertyValue* pMonitor = i_rController.getValue( OUString( "MonitorVisible" ) );
+ if( pMonitor )
+ pMonitor->Value >>= bShowProgressPanel;
+ if( ! i_rController.isShowDialogs() )
+ bShowProgressPanel = sal_False;
+
+ // possibly create one job for collated output
+ sal_Bool bSinglePrintJobs = sal_False;
+ beans::PropertyValue* pSingleValue = i_rController.getValue( OUString( "PrintCollateAsSingleJobs" ) );
+ if( pSingleValue )
+ {
+ pSingleValue->Value >>= bSinglePrintJobs;
+ }
+
+ // FIXME: jobStarted() should be done after the print dialog has ended (if there is one)
+ // how do I know when that might be ?
+ i_rController.jobStarted();
+
+
+ int nCopies = i_rController.getPrinter()->GetCopyCount();
+ int nJobs = 1;
+ if( bSinglePrintJobs )
+ {
+ nJobs = nCopies;
+ nCopies = 1;
+ }
+
+ for( int nCurJob = 0; nCurJob < nJobs; nCurJob++ )
+ {
+ aAccViewState.bNeedRestart = true;
+ do
+ {
+ if( aAccViewState.bNeedRestart )
+ {
+ mnCurPageRangeStart = 0;
+ mnCurPageRangeCount = 0;
+ nAllPages = i_rController.getFilteredPageCount();
+ }
+
+ aAccViewState.bNeedRestart = false;
+
+ Size aCurSize( 21000, 29700 );
+ if( nAllPages > 0 )
+ {
+ mnCurPageRangeCount = 1;
+ aCurSize = getPageSize( i_rController, mnCurPageRangeStart );
+ Size aNextSize( aCurSize );
+
+ // print pages up to a different size
+ while( mnCurPageRangeCount + mnCurPageRangeStart < nAllPages )
+ {
+ aNextSize = getPageSize( i_rController, mnCurPageRangeStart + mnCurPageRangeCount );
+ if( aCurSize == aNextSize // same page size
+ ||
+ (aCurSize.Width() == aNextSize.Height() && aCurSize.Height() == aNextSize.Width()) // same size, but different orientation
+ )
+ {
+ mnCurPageRangeCount++;
+ }
+ else
+ break;
+ }
+ }
+ else
+ mnCurPageRangeCount = 0;
+
+ // now for the current run
+ mnStartPageOffsetX = mnStartPageOffsetY = 0;
+ // setup the paper size and orientation
+ // do this on our associated Printer object, since that is
+ // out interface to the applications which occasionally rely on the paper
+ // information (e.g. brochure printing scales to the found paper size)
+ // also SetPaperSizeUser has the advantage that we can share a
+ // platform independent paper matching algorithm
+ boost::shared_ptr<Printer> pPrinter( i_rController.getPrinter() );
+ pPrinter->SetMapMode( MapMode( MAP_100TH_MM ) );
+ pPrinter->SetPaperSizeUser( aCurSize, true );
+
+ // create view
+ NSView* pPrintView = [[AquaPrintView alloc] initWithController: &i_rController withInfoPrinter: this];
+
+ NSMutableDictionary* pPrintDict = [mpPrintInfo dictionary];
+
+ // set filename
+ if( i_pFileName )
+ {
+ [mpPrintInfo setJobDisposition: NSPrintSaveJob];
+ NSString* pPath = CreateNSString( *i_pFileName );
+ [pPrintDict setObject: pPath forKey: NSPrintSavePath];
+ [pPath release];
+ }
+
+ [pPrintDict setObject: [[NSNumber numberWithInt: nCopies] autorelease] forKey: NSPrintCopies];
+ if( nCopies > 1 )
+ [pPrintDict setObject: [[NSNumber numberWithBool: pPrinter->IsCollateCopy()] autorelease] forKey: NSPrintMustCollate];
+ [pPrintDict setObject: [[NSNumber numberWithBool: YES] autorelease] forKey: NSPrintDetailedErrorReporting];
+ [pPrintDict setObject: [[NSNumber numberWithInt: 1] autorelease] forKey: NSPrintFirstPage];
+ // #i103253# weird: for some reason, autoreleasing the value below like the others above
+ // leads do a double free malloc error. Why this value should behave differently from all the others
+ // is a mystery.
+ [pPrintDict setObject: [NSNumber numberWithInt: mnCurPageRangeCount] forKey: NSPrintLastPage];
+
+
+ // create print operation
+ NSPrintOperation* pPrintOperation = [NSPrintOperation printOperationWithView: pPrintView printInfo: mpPrintInfo];
+
+ if( pPrintOperation )
+ {
+ NSObject* pReleaseAfterUse = nil;
+ bool bShowPanel = !i_rController.isDirectPrint()
+ && (officecfg::Office::Common::Misc::UseSystemPrintDialog::
+ get())
+ && i_rController.isShowDialogs();
+ [pPrintOperation setShowsPrintPanel: bShowPanel ? YES : NO ];
+ [pPrintOperation setShowsProgressPanel: bShowProgressPanel ? YES : NO];
+
+ // set job title (since MacOSX 10.5)
+ if( [pPrintOperation respondsToSelector: @selector(setJobTitle:)] )
+ [pPrintOperation performSelector: @selector(setJobTitle:) withObject: [CreateNSString( i_rJobName ) autorelease]];
+
+ if( bShowPanel && mnCurPageRangeStart == 0 && nCurJob == 0) // only the first range of pages (in the first job) gets the accesory view
+ pReleaseAfterUse = [AquaPrintAccessoryView setupPrinterPanel: pPrintOperation withController: &i_rController withState: &aAccViewState];
+
+ bSuccess = sal_True;
+ mbJob = true;
+ pInst->startedPrintJob();
+ [pPrintOperation runOperation];
+ pInst->endedPrintJob();
+ bWasAborted = [[[pPrintOperation printInfo] jobDisposition] compare: NSPrintCancelJob] == NSOrderedSame;
+ mbJob = false;
+ if( pReleaseAfterUse )
+ [pReleaseAfterUse release];
+ }
+
+ mnCurPageRangeStart += mnCurPageRangeCount;
+ mnCurPageRangeCount = 1;
+ } while( aAccViewState.bNeedRestart || mnCurPageRangeStart + mnCurPageRangeCount < nAllPages );
+ }
+
+ // inform application that it can release its data
+ // this is awkward, but the XRenderable interface has no method for this,
+ // so we need to call XRenderadble::render one last time with IsLastPage = sal_True
+ i_rController.setLastPage( sal_True );
+ GDIMetaFile aPageFile;
+ if( mrContext )
+ SetupPrinterGraphics( mrContext );
+ i_rController.getFilteredPageFile( 0, aPageFile );
+
+ i_rController.setJobState( bWasAborted
+ ? view::PrintableState_JOB_ABORTED
+ : view::PrintableState_JOB_SPOOLED );
+
+ mnCurPageRangeStart = mnCurPageRangeCount = 0;
+
+ return bSuccess;
+}
+
+// -----------------------------------------------------------------------
+
+sal_Bool AquaSalInfoPrinter::EndJob()
+{
+ mnStartPageOffsetX = mnStartPageOffsetY = 0;
+ mbJob = false;
+ return sal_True;
+}
+
+// -----------------------------------------------------------------------
+
+sal_Bool AquaSalInfoPrinter::AbortJob()
+{
+ mbJob = false;
+
+ // FIXME: implementation
+ return sal_False;
+}
+
+// -----------------------------------------------------------------------
+
+SalGraphics* AquaSalInfoPrinter::StartPage( ImplJobSetup* i_pSetupData, sal_Bool i_bNewJobData )
+{
+ if( i_bNewJobData && i_pSetupData )
+ SetPrinterData( i_pSetupData );
+
+ CGContextRef rContext = reinterpret_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]);
+
+ SetupPrinterGraphics( rContext );
+
+ return mpGraphics;
+}
+
+// -----------------------------------------------------------------------
+
+sal_Bool AquaSalInfoPrinter::EndPage()
+{
+ mpGraphics->InvalidateContext();
+ return sal_True;
+}
+
+// -----------------------------------------------------------------------
+
+sal_uLong AquaSalInfoPrinter::GetErrorCode() const
+{
+ return 0;
+}
+
+// =======================================================================
+
+AquaSalPrinter::AquaSalPrinter( AquaSalInfoPrinter* i_pInfoPrinter ) :
+ mpInfoPrinter( i_pInfoPrinter )
+{
+}
+
+// -----------------------------------------------------------------------
+
+AquaSalPrinter::~AquaSalPrinter()
+{
+}
+
+// -----------------------------------------------------------------------
+
+sal_Bool AquaSalPrinter::StartJob( const OUString* i_pFileName,
+ const OUString& i_rJobName,
+ const OUString& i_rAppName,
+ ImplJobSetup* i_pSetupData,
+ vcl::PrinterController& i_rController )
+{
+ return mpInfoPrinter->StartJob( i_pFileName, i_rJobName, i_rAppName, i_pSetupData, i_rController );
+}
+
+// -----------------------------------------------------------------------
+
+sal_Bool AquaSalPrinter::StartJob( const OUString* /*i_pFileName*/,
+ const OUString& /*i_rJobName*/,
+ const OUString& /*i_rAppName*/,
+ sal_uLong /*i_nCopies*/,
+ bool /*i_bCollate*/,
+ bool /*i_bDirect*/,
+ ImplJobSetup* )
+{
+ OSL_FAIL( "should never be called" );
+ return sal_False;
+}
+
+// -----------------------------------------------------------------------
+
+sal_Bool AquaSalPrinter::EndJob()
+{
+ return mpInfoPrinter->EndJob();
+}
+
+// -----------------------------------------------------------------------
+
+sal_Bool AquaSalPrinter::AbortJob()
+{
+ return mpInfoPrinter->AbortJob();
+}
+
+// -----------------------------------------------------------------------
+
+SalGraphics* AquaSalPrinter::StartPage( ImplJobSetup* i_pSetupData, sal_Bool i_bNewJobData )
+{
+ return mpInfoPrinter->StartPage( i_pSetupData, i_bNewJobData );
+}
+
+// -----------------------------------------------------------------------
+
+sal_Bool AquaSalPrinter::EndPage()
+{
+ return mpInfoPrinter->EndPage();
+}
+
+// -----------------------------------------------------------------------
+
+sal_uLong AquaSalPrinter::GetErrorCode()
+{
+ return mpInfoPrinter->GetErrorCode();
+}
+
+void AquaSalInfoPrinter::InitPaperFormats( const ImplJobSetup* )
+{
+ m_aPaperFormats.clear();
+ m_bPapersInit = true;
+
+ if( mpPrinter )
+ {
+ if( [mpPrinter statusForTable: @"PPD"] == NSPrinterTableOK )
+ {
+ NSArray* pPaperNames = [mpPrinter stringListForKey: @"PageSize" inTable: @"PPD"];
+ if( pPaperNames )
+ {
+ unsigned int nPapers = [pPaperNames count];
+ for( unsigned int i = 0; i < nPapers; i++ )
+ {
+ NSString* pPaper = [pPaperNames objectAtIndex: i];
+ // first try to match the name
+ OString aPaperName( [pPaper UTF8String] );
+ Paper ePaper = PaperInfo::fromPSName( aPaperName );
+ if( ePaper != PAPER_USER )
+ {
+ m_aPaperFormats.push_back( PaperInfo( ePaper ) );
+ }
+ else
+ {
+ NSSize aPaperSize = [mpPrinter pageSizeForPaper: pPaper];
+ if( aPaperSize.width > 0 && aPaperSize.height > 0 )
+ {
+ PaperInfo aInfo( PtTo10Mu( aPaperSize.width ),
+ PtTo10Mu( aPaperSize.height ) );
+ if( aInfo.getPaper() == PAPER_USER )
+ aInfo.doSloppyFit();
+ m_aPaperFormats.push_back( aInfo );
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+const PaperInfo* AquaSalInfoPrinter::matchPaper( long i_nWidth, long i_nHeight, Orientation& o_rOrientation ) const
+{
+ if( ! m_bPapersInit )
+ const_cast<AquaSalInfoPrinter*>(this)->InitPaperFormats( NULL );
+
+ const PaperInfo* pMatch = NULL;
+ o_rOrientation = ORIENTATION_PORTRAIT;
+ for( int n = 0; n < 2 ; n++ )
+ {
+ for( size_t i = 0; i < m_aPaperFormats.size(); i++ )
+ {
+ if( abs( m_aPaperFormats[i].getWidth() - i_nWidth ) < 50 &&
+ abs( m_aPaperFormats[i].getHeight() - i_nHeight ) < 50 )
+ {
+ pMatch = &m_aPaperFormats[i];
+ return pMatch;
+ }
+ }
+ o_rOrientation = ORIENTATION_LANDSCAPE;
+ std::swap( i_nWidth, i_nHeight );
+ }
+ return pMatch;
+}
+
+int AquaSalInfoPrinter::GetLandscapeAngle( const ImplJobSetup* )
+{
+ return 900;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/salsys.cxx b/vcl/osx/salsys.cxx
new file mode 100644
index 000000000000..9ac4b5aeb61f
--- /dev/null
+++ b/vcl/osx/salsys.cxx
@@ -0,0 +1,190 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "rtl/ustrbuf.hxx"
+
+#include "vcl/button.hxx"
+
+#include "osx/salsys.h"
+#include "osx/saldata.hxx"
+#include "osx/salinst.h"
+#include "quartz/utils.h"
+
+#include "svids.hrc"
+
+
+// =======================================================================
+
+AquaSalSystem::~AquaSalSystem()
+{
+}
+
+unsigned int AquaSalSystem::GetDisplayScreenCount()
+{
+ NSArray* pScreens = [NSScreen screens];
+ return pScreens ? [pScreens count] : 1;
+}
+
+Rectangle AquaSalSystem::GetDisplayScreenPosSizePixel( unsigned int nScreen )
+{
+ NSArray* pScreens = [NSScreen screens];
+ Rectangle aRet;
+ NSScreen* pScreen = nil;
+ if( pScreens && nScreen < [pScreens count] )
+ pScreen = [pScreens objectAtIndex: nScreen];
+ else
+ pScreen = [NSScreen mainScreen];
+
+ if( pScreen )
+ {
+ NSRect aFrame = [pScreen frame];
+ aRet = Rectangle( Point( static_cast<long int>(aFrame.origin.x), static_cast<long int>(aFrame.origin.y) ),
+ Size( static_cast<long int>(aFrame.size.width), static_cast<long int>(aFrame.size.height) ) );
+ }
+ return aRet;
+}
+
+OUString AquaSalSystem::GetDisplayScreenName( unsigned int nScreen )
+{
+ NSArray* pScreens = [NSScreen screens];
+ OUString aRet;
+ if( nScreen < [pScreens count] )
+ {
+ ResMgr* pMgr = ImplGetResMgr();
+ if( pMgr )
+ {
+ OUString aScreenName(ResId(SV_MAC_SCREENNNAME, *pMgr).toString());
+ aRet = aScreenName.replaceAll("%d", OUString::number(nScreen));
+ }
+ }
+ return aRet;
+}
+
+static NSString* getStandardString( int nButtonId, bool bUseResources )
+{
+ OUString aText;
+ if( bUseResources )
+ {
+ aText = Button::GetStandardText( nButtonId );
+ }
+ if( aText.isEmpty() ) // this is for bad cases, we might be missing the vcl resource
+ {
+ switch( nButtonId )
+ {
+ case BUTTON_OK: aText = "OK";break;
+ case BUTTON_ABORT: aText = "Abort";break;
+ case BUTTON_CANCEL: aText = "Cancel";break;
+ case BUTTON_RETRY: aText = "Retry";break;
+ case BUTTON_YES: aText = "Yes";break;
+ case BUTTON_NO : aText = "No";break;
+ }
+ }
+ return aText.isEmpty() ? nil : CreateNSString( aText);
+}
+
+int AquaSalSystem::ShowNativeMessageBox( const OUString& rTitle,
+ const OUString& rMessage,
+ int nButtonCombination,
+ int nDefaultButton, bool bUseResources)
+{
+ NSString* pTitle = CreateNSString( rTitle );
+ NSString* pMessage = CreateNSString( rMessage );
+
+ struct id_entry
+ {
+ int nCombination;
+ int nDefaultButton;
+ int nTextIds[3];
+ } aButtonIds[] =
+ {
+ { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_OK, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_OK, { BUTTON_OK, -1, -1 } },
+ { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_OK_CANCEL, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_OK, { BUTTON_OK, BUTTON_CANCEL, -1 } },
+ { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_OK_CANCEL, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_CANCEL, { BUTTON_CANCEL, BUTTON_OK, -1 } },
+ { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_ABORT_RETRY_IGNORE, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_ABORT, { BUTTON_ABORT, BUTTON_IGNORE, BUTTON_RETRY } },
+ { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_ABORT_RETRY_IGNORE, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_RETRY, { BUTTON_RETRY, BUTTON_IGNORE, BUTTON_ABORT } },
+ { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_ABORT_RETRY_IGNORE, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_IGNORE, { BUTTON_IGNORE, BUTTON_IGNORE, BUTTON_ABORT } },
+ { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_YES_NO_CANCEL, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_YES, { BUTTON_YES, BUTTON_NO, BUTTON_CANCEL } },
+ { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_YES_NO_CANCEL, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_NO, { BUTTON_NO, BUTTON_YES, BUTTON_CANCEL } },
+ { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_YES_NO_CANCEL, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_CANCEL, { BUTTON_CANCEL, BUTTON_YES, BUTTON_NO } },
+ { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_YES_NO, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_YES, { BUTTON_YES, BUTTON_NO, -1 } },
+ { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_YES_NO, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_NO, { BUTTON_NO, BUTTON_YES, -1 } },
+ { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_RETRY_CANCEL, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_RETRY, { BUTTON_RETRY, BUTTON_CANCEL, -1 } },
+ { SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_RETRY_CANCEL, SALSYSTEM_SHOWNATIVEMSGBOX_BTN_CANCEL, { BUTTON_CANCEL, BUTTON_RETRY, -1 } }
+ };
+
+ NSString* pDefText = nil;
+ NSString* pAltText = nil;
+ NSString* pOthText = nil;
+
+ unsigned int nC;
+ for( nC = 0; nC < sizeof(aButtonIds)/sizeof(aButtonIds[0]); nC++ )
+ {
+ if( aButtonIds[nC].nCombination == nButtonCombination )
+ {
+ if( aButtonIds[nC].nDefaultButton == nDefaultButton )
+ {
+ if( aButtonIds[nC].nTextIds[0] != -1 )
+ pDefText = getStandardString(
+ aButtonIds[nC].nTextIds[0], bUseResources );
+ if( aButtonIds[nC].nTextIds[1] != -1 )
+ pAltText = getStandardString(
+ aButtonIds[nC].nTextIds[1], bUseResources );
+ if( aButtonIds[nC].nTextIds[2] != -1 )
+ pOthText = getStandardString(
+ aButtonIds[nC].nTextIds[2], bUseResources );
+ break;
+ }
+ }
+ }
+
+
+ int nResult = NSRunAlertPanel( pTitle, pMessage, pDefText, pAltText, pOthText );
+
+ if( pTitle )
+ [pTitle release];
+ if( pMessage )
+ [pMessage release];
+ if( pDefText )
+ [pDefText release];
+ if( pAltText )
+ [pAltText release];
+ if( pOthText )
+ [pOthText release];
+
+ int nRet = 0;
+ if( nC < sizeof(aButtonIds)/sizeof(aButtonIds[0]) && nResult >= 1 && nResult <= 3 )
+ {
+ int nPressed = aButtonIds[nC].nTextIds[nResult-1];
+ switch( nPressed )
+ {
+ case BUTTON_NO: nRet = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_NO; break;
+ case BUTTON_YES: nRet = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_YES; break;
+ case BUTTON_OK: nRet = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_OK; break;
+ case BUTTON_CANCEL: nRet = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_CANCEL; break;
+ case BUTTON_ABORT: nRet = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_ABORT; break;
+ case BUTTON_RETRY: nRet = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_RETRY; break;
+ case BUTTON_IGNORE: nRet = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_IGNORE; break;
+ }
+ }
+
+ return nRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/saltimer.cxx b/vcl/osx/saltimer.cxx
new file mode 100644
index 000000000000..ad0959048964
--- /dev/null
+++ b/vcl/osx/saltimer.cxx
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osx/saltimer.h"
+#include "osx/salnstimer.h"
+#include "osx/saldata.hxx"
+#include "osx/salframe.h"
+#include "osx/salinst.h"
+
+// =======================================================================
+
+NSTimer* AquaSalTimer::pRunningTimer = nil;
+bool AquaSalTimer::bDispatchTimer = false;
+
+
+void ImplSalStartTimer( sal_uLong nMS )
+{
+ SalData* pSalData = GetSalData();
+ if( pSalData->mpFirstInstance->isNSAppThread() )
+ {
+ AquaSalTimer::bDispatchTimer = true;
+ NSTimeInterval aTI = double(nMS)/1000.0;
+ if( AquaSalTimer::pRunningTimer != nil )
+ {
+ if( [AquaSalTimer::pRunningTimer timeInterval] == aTI )
+ // set new fire date
+ [AquaSalTimer::pRunningTimer setFireDate: [NSDate dateWithTimeIntervalSinceNow: aTI]];
+ else
+ {
+ [AquaSalTimer::pRunningTimer invalidate];
+ AquaSalTimer::pRunningTimer = nil;
+ }
+ }
+ if( AquaSalTimer::pRunningTimer == nil )
+ {
+ AquaSalTimer::pRunningTimer = [NSTimer scheduledTimerWithTimeInterval: aTI
+ target: [[[TimerCallbackCaller alloc] init] autorelease]
+ selector: @selector(timerElapsed:)
+ userInfo: nil
+ repeats: YES];
+ /* #i84055# add timer to tracking run loop mode,
+ so they also elapse while e.g. life resize
+ */
+ [[NSRunLoop currentRunLoop] addTimer: AquaSalTimer::pRunningTimer forMode: NSEventTrackingRunLoopMode];
+ }
+ }
+ else
+ {
+ SalData::ensureThreadAutoreleasePool();
+ // post an event so we can get into the main thread
+ NSPoint aPt = { 0, 0 };
+ NSEvent* pEvent = [NSEvent otherEventWithType: NSApplicationDefined
+ location: aPt
+ modifierFlags: 0
+ timestamp: [NSDate timeIntervalSinceReferenceDate]
+ windowNumber: 0
+ context: nil
+ subtype: AquaSalInstance::AppStartTimerEvent
+ data1: (int)nMS
+ data2: 0 ];
+ if( pEvent )
+ [NSApp postEvent: pEvent atStart: YES];
+ }
+}
+
+void ImplSalStopTimer()
+{
+ AquaSalTimer::bDispatchTimer = false;
+}
+
+void AquaSalTimer::handleStartTimerEvent( NSEvent* pEvent )
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ if( pSVData->mpSalTimer )
+ {
+ NSTimeInterval posted = [pEvent timestamp] + NSTimeInterval([pEvent data1])/1000.0;
+ NSTimeInterval current = [NSDate timeIntervalSinceReferenceDate];
+ if( (posted - current) <= 0.0 )
+ {
+ YIELD_GUARD;
+ // timer already elapsed since event posted
+ pSVData->mpSalTimer->CallCallback();
+ }
+ ImplSalStartTimer( sal_uLong( [pEvent data1] ) );
+ }
+
+}
+
+AquaSalTimer::AquaSalTimer( )
+{
+}
+
+AquaSalTimer::~AquaSalTimer()
+{
+ ImplSalStopTimer();
+}
+
+void AquaSalTimer::Start( sal_uLong nMS )
+{
+ ImplSalStartTimer( nMS );
+}
+
+void AquaSalTimer::Stop()
+{
+ ImplSalStopTimer();
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/salvd.cxx b/vcl/osx/salvd.cxx
new file mode 100644
index 000000000000..3f69ab9f5f7c
--- /dev/null
+++ b/vcl/osx/salvd.cxx
@@ -0,0 +1,261 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "vcl/svapp.hxx"
+#include "vcl/sysdata.hxx"
+
+#include "osx/salvd.h"
+#include "osx/salinst.h"
+#include "quartz/salgdi.h"
+#include "osx/saldata.hxx"
+#include "osx/salframe.h"
+
+// -----------------------------------------------------------------------
+
+SalVirtualDevice* AquaSalInstance::CreateVirtualDevice( SalGraphics* pGraphics,
+ long nDX, long nDY, sal_uInt16 nBitCount, const SystemGraphicsData *pData )
+{
+ // #i92075# can be called first in a thread
+ SalData::ensureThreadAutoreleasePool();
+
+ return new AquaSalVirtualDevice( static_cast< AquaSalGraphics* >( pGraphics ), nDX, nDY, nBitCount, pData );
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalInstance::DestroyVirtualDevice( SalVirtualDevice* pDevice )
+{
+ delete pDevice;
+}
+
+// =======================================================================
+
+AquaSalVirtualDevice::AquaSalVirtualDevice( AquaSalGraphics* pGraphic, long nDX, long nDY, sal_uInt16 nBitCount, const SystemGraphicsData *pData )
+: mbGraphicsUsed( false )
+, mxBitmapContext( NULL )
+, mnBitmapDepth( 0 )
+, mxLayer( NULL )
+{
+ if( pGraphic && pData && pData->rCGContext )
+ {
+ // Create virtual device based on existing SystemGraphicsData
+ // We ignore nDx and nDY, as the desired size comes from the SystemGraphicsData
+ mbForeignContext = true; // the mxContext is from pData
+ mpGraphics = new AquaSalGraphics( /*pGraphic*/ );
+ mpGraphics->SetVirDevGraphics( mxLayer, pData->rCGContext );
+ }
+ else
+ {
+ // create empty new virtual device
+ mbForeignContext = false; // the mxContext is created within VCL
+ mpGraphics = new AquaSalGraphics(); // never fails
+ mnBitmapDepth = nBitCount;
+
+ // inherit resolution from reference device
+ if( pGraphic )
+ {
+ AquaSalFrame* pFrame = pGraphic->getGraphicsFrame();
+ if( pFrame && AquaSalFrame::isAlive( pFrame ) )
+ {
+ mpGraphics->setGraphicsFrame( pFrame );
+ mpGraphics->copyResolution( *pGraphic );
+ }
+ }
+
+ if( nDX && nDY )
+ SetSize( nDX, nDY );
+
+ // NOTE: if SetSize does not succeed, we just ignore the nDX and nDY
+ }
+}
+
+// -----------------------------------------------------------------------
+
+AquaSalVirtualDevice::~AquaSalVirtualDevice()
+{
+ if( mpGraphics )
+ {
+ mpGraphics->SetVirDevGraphics( NULL, NULL );
+ delete mpGraphics;
+ mpGraphics = 0;
+ }
+ Destroy();
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalVirtualDevice::Destroy()
+{
+ if( mbForeignContext ) {
+ // Do not delete mxContext that we have received from outside VCL
+ mxLayer = NULL;
+ return;
+ }
+
+ if( mxLayer )
+ {
+ if( mpGraphics )
+ mpGraphics->SetVirDevGraphics( NULL, NULL );
+ CGLayerRelease( mxLayer );
+ mxLayer = NULL;
+ }
+
+ if( mxBitmapContext )
+ {
+ void* pRawData = CGBitmapContextGetData( mxBitmapContext );
+ rtl_freeMemory( pRawData );
+ CGContextRelease( mxBitmapContext );
+ mxBitmapContext = NULL;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+SalGraphics* AquaSalVirtualDevice::GetGraphics()
+{
+ if( mbGraphicsUsed || !mpGraphics )
+ return 0;
+
+ mbGraphicsUsed = true;
+ return mpGraphics;
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalVirtualDevice::ReleaseGraphics( SalGraphics* )
+{
+ mbGraphicsUsed = false;
+}
+
+// -----------------------------------------------------------------------
+
+sal_Bool AquaSalVirtualDevice::SetSize( long nDX, long nDY )
+{
+ if( mbForeignContext )
+ {
+ // Do not delete/resize mxContext that we have received from outside VCL
+ return true;
+ }
+
+ if( mxLayer )
+ {
+ const CGSize aSize = CGLayerGetSize( mxLayer );
+ if( (nDX == aSize.width) && (nDY == aSize.height) )
+ {
+ // Yay, we do not have to do anything :)
+ return true;
+ }
+ }
+
+ Destroy();
+
+ // create a Quartz layer matching to the intended virdev usage
+ CGContextRef xCGContext = NULL;
+ if( mnBitmapDepth && (mnBitmapDepth < 16) )
+ {
+ mnBitmapDepth = 8; // TODO: are 1bit vdevs worth it?
+ const CGColorSpaceRef aCGColorSpace = GetSalData()->mxGraySpace;
+ const CGBitmapInfo aCGBmpInfo = kCGImageAlphaNone;
+ const int nBytesPerRow = (mnBitmapDepth * nDX + 7) / 8;
+
+ void* pRawData = rtl_allocateMemory( nBytesPerRow * nDY );
+ mxBitmapContext = ::CGBitmapContextCreate( pRawData, nDX, nDY,
+ mnBitmapDepth, nBytesPerRow, aCGColorSpace, aCGBmpInfo );
+ xCGContext = mxBitmapContext;
+ }
+ else
+ {
+ // default to a NSView target context
+ AquaSalFrame* pSalFrame = mpGraphics->getGraphicsFrame();
+ if( !pSalFrame || !AquaSalFrame::isAlive( pSalFrame ))
+ {
+ if( !GetSalData()->maFrames.empty() )
+ {
+ // get the first matching frame
+ pSalFrame = *GetSalData()->maFrames.begin();
+ }
+ else
+ {
+ // ensure we don't reuse a dead AquaSalFrame on the very
+ // unlikely case of no other frame to use
+ pSalFrame = NULL;
+ }
+ // update the frame reference
+ mpGraphics->setGraphicsFrame( pSalFrame );
+ }
+ if( pSalFrame )
+ {
+ // #i91990#
+ NSWindow* pNSWindow = pSalFrame->getNSWindow();
+ if ( pNSWindow )
+ {
+ NSGraphicsContext* pNSContext = [NSGraphicsContext graphicsContextWithWindow: pNSWindow];
+ if( pNSContext )
+ xCGContext = reinterpret_cast<CGContextRef>([pNSContext graphicsPort]);
+ }
+ else
+ {
+ // fall back to a bitmap context
+ mnBitmapDepth = 32;
+ const CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace;
+ const CGBitmapInfo aCGBmpInfo = kCGImageAlphaNoneSkipFirst;
+ const int nBytesPerRow = (mnBitmapDepth * nDX) / 8;
+
+ void* pRawData = rtl_allocateMemory( nBytesPerRow * nDY );
+ mxBitmapContext = ::CGBitmapContextCreate( pRawData, nDX, nDY,
+ 8, nBytesPerRow, aCGColorSpace, aCGBmpInfo );
+ xCGContext = mxBitmapContext;
+ }
+ }
+ }
+
+ DBG_ASSERT( xCGContext, "no context" );
+
+ const CGSize aNewSize = { static_cast<CGFloat>(nDX), static_cast<CGFloat>(nDY) };
+ mxLayer = CGLayerCreateWithContext( xCGContext, aNewSize, NULL );
+
+ if( mxLayer && mpGraphics )
+ {
+ // get the matching Quartz context
+ CGContextRef xDrawContext = CGLayerGetContext( mxLayer );
+ mpGraphics->SetVirDevGraphics( mxLayer, xDrawContext, mnBitmapDepth );
+ }
+
+ return (mxLayer != NULL);
+}
+
+// -----------------------------------------------------------------------
+
+void AquaSalVirtualDevice::GetSize( long& rWidth, long& rHeight )
+{
+ if( mxLayer )
+ {
+ const CGSize aSize = CGLayerGetSize( mxLayer );
+ rWidth = static_cast<long>(aSize.width);
+ rHeight = static_cast<long>(aSize.height);
+ }
+ else
+ {
+ rWidth = 0;
+ rHeight = 0;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/service_entry.cxx b/vcl/osx/service_entry.cxx
new file mode 100644
index 000000000000..d4fa284c2ddb
--- /dev/null
+++ b/vcl/osx/service_entry.cxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "osl/diagnose.h"
+
+#include "vcl/svapp.hxx"
+
+#include "osx/saldata.hxx"
+#include "osx/salinst.h"
+
+#include "DragSource.hxx"
+#include "DropTarget.hxx"
+#include "clipboard.hxx"
+
+using namespace ::osl;
+using namespace ::rtl;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::cppu;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::datatransfer::clipboard;
+
+
+uno::Reference< XInterface > AquaSalInstance::CreateClipboard( const Sequence< Any >& i_rArguments )
+{
+ if ( Application::IsHeadlessModeEnabled() )
+ return SalInstance::CreateClipboard( i_rArguments );
+
+ SalData* pSalData = GetSalData();
+ if( ! pSalData->mxClipboard.is() )
+ pSalData->mxClipboard = uno::Reference<XInterface>(static_cast< XClipboard* >(new AquaClipboard()), UNO_QUERY);
+ return pSalData->mxClipboard;
+}
+
+uno::Reference<XInterface> AquaSalInstance::CreateDragSource()
+{
+ if ( Application::IsHeadlessModeEnabled() )
+ return SalInstance::CreateDragSource();
+
+ return uno::Reference<XInterface>(static_cast< XInitialization* >(new DragSource()), UNO_QUERY);
+}
+
+uno::Reference<XInterface> AquaSalInstance::CreateDropTarget()
+{
+ if ( Application::IsHeadlessModeEnabled() )
+ return SalInstance::CreateDropTarget();
+
+ return uno::Reference<XInterface>(static_cast< XInitialization* >(new DropTarget()), UNO_QUERY);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/vclnsapp.mm b/vcl/osx/vclnsapp.mm
new file mode 100644
index 000000000000..18079fb1ca9b
--- /dev/null
+++ b/vcl/osx/vclnsapp.mm
@@ -0,0 +1,517 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#include "sal/config.h"
+
+#include <vector>
+
+#include "vcl/window.hxx"
+#include "vcl/svapp.hxx"
+#include "vcl/cmdevt.hxx"
+
+#include "osx/vclnsapp.h"
+#include "osx/salinst.h"
+#include "osx/saldata.hxx"
+#include "osx/salframe.h"
+#include "osx/salframeview.h"
+#include "quartz/utils.h"
+
+#include "impimagetree.hxx"
+
+#include "premac.h"
+#include <objc/objc-runtime.h>
+#import "Carbon/Carbon.h"
+#import "apple_remote/RemoteControl.h"
+#include "postmac.h"
+
+
+@implementation CocoaThreadEnabler
+-(void)enableCocoaThreads:(id)param
+{
+ // do nothing, this is just to start an NSThread and therefore put
+ // Cocoa into multithread mode
+ (void)param;
+}
+@end
+
+// If you wonder how this VCL_NSApplication stuff works, one thing you
+// might have missed is that the NSPrincipalClass property in
+// desktop/macosx/Info.plist has the value VCL_NSApplication.
+
+@implementation VCL_NSApplication
+-(void)sendEvent:(NSEvent*)pEvent
+{
+ NSEventType eType = [pEvent type];
+ if( eType == NSApplicationDefined )
+ GetSalData()->mpFirstInstance->handleAppDefinedEvent( pEvent );
+ else if( eType == NSKeyDown && ([pEvent modifierFlags] & NSCommandKeyMask) != 0 )
+ {
+ NSWindow* pKeyWin = [NSApp keyWindow];
+ if( pKeyWin && [pKeyWin isKindOfClass: [SalFrameWindow class]] )
+ {
+ AquaSalFrame* pFrame = [(SalFrameWindow*)pKeyWin getSalFrame];
+ // handle Cmd-W
+ // FIXME: the correct solution would be to handle this in framework
+ // in the menu code
+ // however that is currently being revised, so let's use a preliminary solution here
+ // this hack is based on assumption
+ // a) Cmd-W is the same in all languages in OOo's menu conig
+ // b) Cmd-W is the same in all languages in on MacOS
+ // for now this seems to be true
+ unsigned int nModMask = ([pEvent modifierFlags] & (NSShiftKeyMask|NSControlKeyMask|NSAlternateKeyMask|NSCommandKeyMask));
+ if( (pFrame->mnStyleMask & NSClosableWindowMask) != 0 )
+ {
+ if( nModMask == NSCommandKeyMask
+ && [[pEvent charactersIgnoringModifiers] isEqualToString: @"w"] )
+ {
+ // Note: gcc 4.2.1 (in the 10.6 SDK) tells us
+ // 'NSWindow' may not respond to
+ // '-windowShouldClose:' . Is that a bogus
+ // warning, or is this code bogus? No idea.
+ // Anyway, so that we can compile also against
+ // this SDK with -Werror, use objc_msgSend
+ // instead.
+
+ // Instead of:
+ // [pFrame->getNSWindow() windowShouldClose: nil];
+ // do:
+ objc_msgSend(pFrame->getNSWindow(), @selector(windowShouldClose:), nil);
+
+ return;
+ }
+ }
+
+ /*
+ * #i98949# - Cmd-M miniaturize window, Cmd-Option-M miniaturize all windows
+ */
+ if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"m"] )
+ {
+ if ( nModMask == NSCommandKeyMask && ([pFrame->getNSWindow() styleMask] & NSMiniaturizableWindowMask) )
+ {
+ [pFrame->getNSWindow() performMiniaturize: nil];
+ return;
+ }
+
+ if ( nModMask == ( NSCommandKeyMask | NSAlternateKeyMask ) )
+ {
+ [NSApp miniaturizeAll: nil];
+ return;
+ }
+ }
+
+ // #i90083# handle frame switching
+ // FIXME: lousy workaround
+ if( (nModMask & (NSControlKeyMask|NSAlternateKeyMask)) == 0 )
+ {
+ if( [[pEvent characters] isEqualToString: @"<"] ||
+ [[pEvent characters] isEqualToString: @"~"] )
+ {
+ [self cycleFrameForward: pFrame];
+ return;
+ }
+ else if( [[pEvent characters] isEqualToString: @">"] ||
+ [[pEvent characters] isEqualToString: @"`"] )
+ {
+ [self cycleFrameBackward: pFrame];
+ return;
+ }
+ }
+
+ // get information whether the event was handled; keyDown returns nothing
+ GetSalData()->maKeyEventAnswer[ pEvent ] = false;
+ bool bHandled = false;
+
+ // dispatch to view directly to avoid the key event being consumed by the menubar
+ // popup windows do not get the focus, so they don't get these either
+ // simplest would be dispatch this to the key window always if it is without parent
+ // however e.g. in document we want the menu shortcut if e.g. the stylist has focus
+ if( pFrame->mpParent && (pFrame->mnStyle & SAL_FRAME_STYLE_FLOAT) == 0 )
+ {
+ [[pKeyWin contentView] keyDown: pEvent];
+ bHandled = GetSalData()->maKeyEventAnswer[ pEvent ];
+ }
+
+ // see whether the main menu consumes this event
+ // if not, we want to dispatch it ourselves. Unless we do this "trick"
+ // the main menu just beeps for an unknown or disabled key equivalent
+ // and swallows the event wholesale
+ NSMenu* pMainMenu = [NSApp mainMenu];
+ if( ! bHandled && (pMainMenu == 0 || ! [pMainMenu performKeyEquivalent: pEvent]) )
+ {
+ [[pKeyWin contentView] keyDown: pEvent];
+ bHandled = GetSalData()->maKeyEventAnswer[ pEvent ];
+ }
+ else
+ bHandled = true; // event handled already or main menu just handled it
+
+ GetSalData()->maKeyEventAnswer.erase( pEvent );
+ if( bHandled )
+ return;
+ }
+ else if( pKeyWin )
+ {
+ // #i94601# a window not of vcl's making has the focus.
+ // Since our menus do not invoke the usual commands
+ // try to play nice with native windows like the file dialog
+ // and emulate them
+ // precondition: this ONLY works because CMD-V (paste), CMD-C (copy) and CMD-X (cut) are
+ // NOT localized, that is the same in all locales. Should this be
+ // different in any locale, this hack will fail.
+ unsigned int nModMask = ([pEvent modifierFlags] & (NSShiftKeyMask|NSControlKeyMask|NSAlternateKeyMask|NSCommandKeyMask));
+ if( nModMask == NSCommandKeyMask )
+ {
+
+ if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"v"] )
+ {
+ if( [NSApp sendAction: @selector(paste:) to: nil from: nil] )
+ return;
+ }
+ else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"c"] )
+ {
+ if( [NSApp sendAction: @selector(copy:) to: nil from: nil] )
+ return;
+ }
+ else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"x"] )
+ {
+ if( [NSApp sendAction: @selector(cut:) to: nil from: nil] )
+ return;
+ }
+ }
+ }
+ }
+ [super sendEvent: pEvent];
+}
+
+-(void)sendSuperEvent:(NSEvent*)pEvent
+{
+ [super sendEvent: pEvent];
+}
+
+-(void)cycleFrameForward: (AquaSalFrame*)pCurFrame
+{
+ // find current frame in list
+ std::list< AquaSalFrame* >& rFrames( GetSalData()->maFrames );
+ std::list< AquaSalFrame* >::iterator it = rFrames.begin();
+ for( ; it != rFrames.end() && *it != pCurFrame; ++it )
+ ;
+ if( it != rFrames.end() )
+ {
+ // now find the next frame (or end)
+ do
+ {
+ ++it;
+ if( it != rFrames.end() )
+ {
+ if( (*it)->mpDockMenuEntry != NULL &&
+ (*it)->mbShown )
+ {
+ [(*it)->getNSWindow() makeKeyAndOrderFront: NSApp];
+ return;
+ }
+ }
+ } while( it != rFrames.end() );
+ // cycle around, find the next up to pCurFrame
+ it = rFrames.begin();
+ while( *it != pCurFrame )
+ {
+ if( (*it)->mpDockMenuEntry != NULL &&
+ (*it)->mbShown )
+ {
+ [(*it)->getNSWindow() makeKeyAndOrderFront: NSApp];
+ return;
+ }
+ ++it;
+ }
+ }
+}
+
+-(void)cycleFrameBackward: (AquaSalFrame*)pCurFrame
+{
+ // do the same as cycleFrameForward only with a reverse iterator
+
+ // find current frame in list
+ std::list< AquaSalFrame* >& rFrames( GetSalData()->maFrames );
+ std::list< AquaSalFrame* >::reverse_iterator it = rFrames.rbegin();
+ for( ; it != rFrames.rend() && *it != pCurFrame; ++it )
+ ;
+ if( it != rFrames.rend() )
+ {
+ // now find the next frame (or end)
+ do
+ {
+ ++it;
+ if( it != rFrames.rend() )
+ {
+ if( (*it)->mpDockMenuEntry != NULL &&
+ (*it)->mbShown )
+ {
+ [(*it)->getNSWindow() makeKeyAndOrderFront: NSApp];
+ return;
+ }
+ }
+ } while( it != rFrames.rend() );
+ // cycle around, find the next up to pCurFrame
+ it = rFrames.rbegin();
+ while( *it != pCurFrame )
+ {
+ if( (*it)->mpDockMenuEntry != NULL &&
+ (*it)->mbShown )
+ {
+ [(*it)->getNSWindow() makeKeyAndOrderFront: NSApp];
+ return;
+ }
+ ++it;
+ }
+ }
+}
+
+-(NSMenu*)applicationDockMenu:(NSApplication *)sender
+{
+ (void)sender;
+ return AquaSalInstance::GetDynamicDockMenu();
+}
+
+-(BOOL)application: (NSApplication*)app openFile: (NSString*)pFile
+{
+ (void)app;
+ std::vector<OUString> aFile;
+ aFile.push_back( GetOUString( pFile ) );
+ if( ! AquaSalInstance::isOnCommandLine( aFile[0] ) )
+ {
+ const ApplicationEvent* pAppEvent = new ApplicationEvent(ApplicationEvent::TYPE_OPEN, aFile);
+ AquaSalInstance::aAppEventList.push_back( pAppEvent );
+ }
+ return YES;
+}
+
+-(void)application: (NSApplication*) app openFiles: (NSArray*)files
+{
+ (void)app;
+ std::vector<OUString> aFileList;
+
+ NSEnumerator* it = [files objectEnumerator];
+ NSString* pFile = nil;
+
+ while( (pFile = [it nextObject]) != nil )
+ {
+ const rtl::OUString aFile( GetOUString( pFile ) );
+ if( ! AquaSalInstance::isOnCommandLine( aFile ) )
+ {
+ aFileList.push_back( aFile );
+ }
+ }
+
+ if( !aFileList.empty() )
+ {
+ // we have no back channel here, we have to assume success, in which case
+ // replyToOpenOrPrint does not need to be called according to documentation
+ // [app replyToOpenOrPrint: NSApplicationDelegateReplySuccess];
+ const ApplicationEvent* pAppEvent = new ApplicationEvent(ApplicationEvent::TYPE_OPEN, aFileList);
+ AquaSalInstance::aAppEventList.push_back( pAppEvent );
+ }
+}
+
+-(BOOL)application: (NSApplication*)app printFile: (NSString*)pFile
+{
+ (void)app;
+ std::vector<OUString> aFile;
+ aFile.push_back( GetOUString( pFile ) );
+ const ApplicationEvent* pAppEvent = new ApplicationEvent(ApplicationEvent::TYPE_PRINT, aFile);
+ AquaSalInstance::aAppEventList.push_back( pAppEvent );
+ return YES;
+}
+-(NSApplicationPrintReply)application: (NSApplication *) app printFiles:(NSArray *)files withSettings: (NSDictionary *)printSettings showPrintPanels:(BOOL)bShowPrintPanels
+{
+ (void)app;
+ (void)printSettings;
+ (void)bShowPrintPanels;
+ // currently ignores print settings an bShowPrintPanels
+ std::vector<OUString> aFileList;
+
+ NSEnumerator* it = [files objectEnumerator];
+ NSString* pFile = nil;
+
+ while( (pFile = [it nextObject]) != nil )
+ {
+ aFileList.push_back( GetOUString( pFile ) );
+ }
+ const ApplicationEvent* pAppEvent = new ApplicationEvent(ApplicationEvent::TYPE_PRINT, aFileList);
+ AquaSalInstance::aAppEventList.push_back( pAppEvent );
+ // we have no back channel here, we have to assume success
+ // correct handling would be NSPrintingReplyLater and then send [app replyToOpenOrPrint]
+ return NSPrintingSuccess;
+}
+
+-(NSApplicationTerminateReply)applicationShouldTerminate: (NSApplication *) app
+{
+ (void)app;
+ NSApplicationTerminateReply aReply = NSTerminateNow;
+ {
+ YIELD_GUARD;
+
+ SalData* pSalData = GetSalData();
+ if( ! pSalData->maFrames.empty() )
+ {
+ // the following QueryExit will likely present a message box, activate application
+ [NSApp activateIgnoringOtherApps: YES];
+ aReply = pSalData->maFrames.front()->CallCallback( SALEVENT_SHUTDOWN, NULL ) ? NSTerminateCancel : NSTerminateNow;
+ }
+
+ if( aReply == NSTerminateNow )
+ {
+ ApplicationEvent aEv(ApplicationEvent::TYPE_PRIVATE_DOSHUTDOWN);
+ GetpApp()->AppEvent( aEv );
+ ImplImageTreeSingletonRef()->shutDown();
+ // DeInitVCL should be called in ImplSVMain - unless someon _exits first which
+ // can occur in Desktop::doShutdown for example
+ }
+ }
+
+ return aReply;
+}
+
+-(void)systemColorsChanged: (NSNotification*) pNotification
+{
+ (void)pNotification;
+ YIELD_GUARD;
+
+ const SalData* pSalData = GetSalData();
+ if( !pSalData->maFrames.empty() )
+ pSalData->maFrames.front()->CallCallback( SALEVENT_SETTINGSCHANGED, NULL );
+}
+
+-(void)screenParametersChanged: (NSNotification*) pNotification
+{
+ (void)pNotification;
+ YIELD_GUARD;
+
+ SalData* pSalData = GetSalData();
+ std::list< AquaSalFrame* >::iterator it;
+ for( it = pSalData->maFrames.begin(); it != pSalData->maFrames.end(); ++it )
+ {
+ (*it)->screenParametersChanged();
+ }
+}
+
+-(void)scrollbarVariantChanged: (NSNotification*) pNotification
+{
+ (void)pNotification;
+ GetSalData()->mpFirstInstance->delayedSettingsChanged( true );
+}
+
+-(void)scrollbarSettingsChanged: (NSNotification*) pNotification
+{
+ (void)pNotification;
+ GetSalData()->mpFirstInstance->delayedSettingsChanged( false );
+}
+
+-(void)addFallbackMenuItem: (NSMenuItem*)pNewItem
+{
+ AquaSalMenu::addFallbackMenuItem( pNewItem );
+}
+
+-(void)removeFallbackMenuItem: (NSMenuItem*)pItem
+{
+ AquaSalMenu::removeFallbackMenuItem( pItem );
+}
+
+-(void)addDockMenuItem: (NSMenuItem*)pNewItem
+{
+ NSMenu* pDock = AquaSalInstance::GetDynamicDockMenu();
+ [pDock insertItem: pNewItem atIndex: [pDock numberOfItems]];
+}
+
+// for Apple Remote implementation
+
+#if !HAVE_FEATURE_MACOSX_SANDBOX
+- (void)applicationWillBecomeActive:(NSNotification *)pNotification
+{
+ (void)pNotification;
+ SalData* pSalData = GetSalData();
+ if( pSalData->mpMainController && pSalData->mpMainController->remoteControl)
+ {
+ // [remoteControl startListening: self];
+ // does crash because the right thing to do is
+ // [GetSalData()->mpMainController->remoteControl startListening: self];
+ // but the instance variable 'remoteControl' is declared protected
+ // workaround : declare remoteControl instance variable as public in RemoteMainController.m
+
+ [pSalData->mpMainController->remoteControl startListening: self];
+#ifdef DEBUG
+ NSLog(@"Apple Remote will become active - Using remote controls");
+#endif
+ }
+ for( std::list< AquaSalFrame* >::const_iterator it = pSalData->maPresentationFrames.begin();
+ it != pSalData->maPresentationFrames.end(); ++it )
+ {
+ NSWindow* pNSWindow = (*it)->getNSWindow();
+ [pNSWindow setLevel: NSPopUpMenuWindowLevel];
+ if( [pNSWindow isVisible] )
+ [pNSWindow orderFront: NSApp];
+ }
+}
+
+- (void)applicationWillResignActive:(NSNotification *)pNotification
+{
+ (void)pNotification;
+ SalData* pSalData = GetSalData();
+ if( pSalData->mpMainController && pSalData->mpMainController->remoteControl)
+ {
+ // [remoteControl stopListening: self];
+ // does crash because the right thing to do is
+ // [GetSalData()->mpMainController->remoteControl stopListening: self];
+ // but the instance variable 'remoteControl' is declared protected
+ // workaround : declare remoteControl instance variable as public in RemoteMainController.m
+
+ [pSalData->mpMainController->remoteControl stopListening: self];
+#ifdef DEBUG
+ NSLog(@"Apple Remote will resign active - Releasing remote controls");
+#endif
+ }
+ for( std::list< AquaSalFrame* >::const_iterator it = pSalData->maPresentationFrames.begin();
+ it != pSalData->maPresentationFrames.end(); ++it )
+ {
+ [(*it)->getNSWindow() setLevel: NSNormalWindowLevel];
+ }
+}
+#endif
+
+- (BOOL)applicationShouldHandleReopen: (NSApplication*)pApp hasVisibleWindows: (BOOL) bWinVisible
+{
+ (void)pApp;
+ (void)bWinVisible;
+ NSObject* pHdl = GetSalData()->mpDockIconClickHandler;
+ if( pHdl && [pHdl respondsToSelector: @selector(dockIconClicked:)] )
+ {
+ [pHdl performSelector:@selector(dockIconClicked:) withObject: self];
+ }
+ return YES;
+}
+
+-(void)setDockIconClickHandler: (NSObject*)pHandler
+{
+ GetSalData()->mpDockIconClickHandler = pHandler;
+}
+
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */